Skip to content

Unhelpful diagnostics when creating or updating Rules #829

@orangebucket

Description

@orangebucket

Describe the bug
Version 2.1.1 (and earlier 2.x.x) seems to have drastically changed it's error handling. I have written this bug report in the context of issues I had creating and updating Rules but since then I have seen that similar issues with hundreds of lines of error messages seem to occur with any 4xx error.

For example, today I was trying to create Rules that had two errors I wasn't aware of.

  1. An incorrect device ID.
  2. An invalid component name for a given device.

Previously I would have received an error response that made it reasonably clear that a device ID didn't exist or an invalid component had been used. That simply isn't there. I believe this part may be the same issue that has already been reported in #828. However it is far worse than that ...

What I get instead is several 'screens' worth of protocol headers and stack traces and the like which to me has no diagnostic value whatsoever. The most I can establish is that the HTTP codes are 1) 403 and 2) 422, the rest adds nothing.

To Reproduce
Deliberately create or modify a Rule to use a non-existent device ID or an invalid component name and then use the CLI to create or update the Rule.

Expected behavior
I expect to receive the response from the API.

Actual behavior
The CLI spews its guts out for no obvious reason. If the error response is in there somewhere it is buried and incomplete.

Curiously the output starts as if the issued command was 'smartthings rules:create' without arguments, so I get lectured on the command syntax, and then it just barfs.

Additional context

Here is a simple example with a fake device ID.

> smartthings rules:create -i anerrortest.yaml
─────────────────────────────────────────────────
 #  Name    Location Id
─────────────────────────────────────────────────
 1  Anidea  REDACTED
─────────────────────────────────────────────────
√ Select a location. 1
smartthings rules:create

create a rule

Flags:
      --help         Show help                                                                                 [boolean]
      --version      Show version number                                                                       [boolean]
  -p, --profile      configuration profile                                                 [string] [default: "default"]
      --environment  the environment to use                                        [string] [choices: "global", "china"]
      --language     ISO language code or "NONE" to not specify a language. Defaults to the OS locale           [string]
  -o, --output       specify output file                                                                        [string]
  -j, --json         use JSON format of input and/or output                                                    [boolean]
  -y, --yaml         use YAML format of input and/or output                                                    [boolean]
  -i, --input        specify input file                                                                         [string]
  -d, --dry-run      produce JSON but don't actually submit                                                    [boolean]
  -l, --location     the location for the rule                                                                  [string]

Examples:
  smartthings rules:create -i my-rule.yaml  create a rule defined in "my-rule.yaml"

For API information, see:
  https://developer.smartthings.com/docs/api/public/#operation/createRule

AxiosError: Request failed with status code 403
    at settle (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:147175:12)
    at Unzip.handleStreamEnd (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:148565:11)
    at Unzip.emit (node:events:508:28)
    at endReadableNT (node:internal/streams/readable:1701:12)
    at process.processTicksAndRejections (node:internal/process/task_queues:90:21)
    at Axios.request (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:149802:41)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async EndpointClient.request (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:16987:30)
    at async inputAndOutputItem (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:167883:22)
    at async Object.rules_create_handler [as handler] (file:///C:/Program%20Files/SmartThings/dist_bin/smartthings.mjs:177011:5) {
  isAxiosError: true,
  code: 'ERR_BAD_REQUEST',
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [ 'xhr', 'http', 'fetch' ],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    env: { FormData: [Function], Blob: [class Blob] },
    validateStatus: [Function: validateStatus],
    headers: Object [AxiosHeaders] {
      Accept: 'application/json',
      'Content-Type': 'application/json;charset=utf-8',
      'User-Agent': '@smartthings/cli',
      'Accept-Language': 'en-GB',
      Authorization: 'Bearer REDACTED',
      'Content-Length': '428',
      'Accept-Encoding': 'gzip, compress, deflate, br'
    },
    url: 'https://api.smartthings.com/rules',
    method: 'post',
    params: { locationId: 'REDACTED' },
    data: '{"name":"[R] Auto-off: Bedroom: Fan","actions":[{"if":{"remains":{"equals":{"left":{"device":{"devices":["12345678-9ABC-DEF0-1234-45689ABC"],"component":"main","capability":"switch","attribute":"switch"}},"right":{"string":"on"}},"duration":{"value":{"integer":45},"unit":"Minute"}},"then":[{"command":{"devices":["12345678-9ABC-DEF0-1234-45689ABC"],"commands":[{"component":"main","capability":"switch","command":"off"}]}}]}}]}',
    paramsSerializer: { serialize: [Function: serialize] },
    allowAbsoluteUrls: true
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: true,
    _last: false,
    chunkedEncoding: false,
    shouldKeepAlive: true,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: 428,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: true,
    _header: 'POST /rules?locationId=REDACTED HTTP/1.1\r\n' +
      'Accept: application/json\r\n' +
      'Content-Type: application/json;charset=utf-8\r\n' +
      'User-Agent: @smartthings/cli\r\n' +
      'Accept-Language: en-GB\r\n' +
      'Authorization: Bearer REDACTED\r\n' +
      'Content-Length: 428\r\n' +
      'Accept-Encoding: gzip, compress, deflate, br\r\n' +
      'Host: api.smartthings.com\r\n' +
      'Connection: keep-alive\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      options: [Object: null prototype],
      defaultPort: 443,
      protocol: 'https:',
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype] {},
      freeSockets: [Object: null prototype],
      keepAliveMsecs: 1000,
      keepAlive: true,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 1,
      agentKeepAliveTimeoutBuffer: 1000,
      maxCachedSessions: 100,
      _sessionCache: [Object],
      Symbol(shapeMode): false,
      Symbol(kCapture): false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/rules?locationId=REDACTED',
    _ended: true,
    res: IncomingMessage {
      _events: [Object],
      _readableState: [ReadableState],
      _maxListeners: undefined,
      socket: null,
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 403,
      statusMessage: 'Forbidden',
      client: [TLSSocket],
      _consuming: true,
      _dumped: false,
      req: [Circular *1],
      _eventsCount: 4,
      responseUrl: 'https://api.smartthings.com/rules?locationId=REDACTED',
      redirects: [],
      Symbol(shapeMode): true,
      Symbol(kCapture): false,
      Symbol(kHeaders): [Object],
      Symbol(kHeadersCount): 20,
      Symbol(kTrailers): null,
      Symbol(kTrailersCount): 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'api.smartthings.com',
    protocol: 'https:',
    _redirectable: Writable {
      _events: [Object],
      _writableState: [WritableState],
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 428,
      _requestBodyBuffers: [],
      _eventsCount: 3,
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'https://api.smartthings.com/rules?locationId=REDACTED',
      _timeout: null,
      Symbol(shapeMode): true,
      Symbol(kCapture): false
    },
    Symbol(shapeMode): false,
    Symbol(kCapture): false,
    Symbol(kBytesWritten): 0,
    Symbol(kNeedDrain): false,
    Symbol(corked): 0,
    Symbol(kChunkedBuffer): [],
    Symbol(kChunkedLength): 0,
    Symbol(kSocket): TLSSocket {
      _tlsOptions: [Object],
      _secureEstablished: true,
      _securePending: false,
      _newSessionPending: false,
      _controlReleased: true,
      secureConnecting: false,
      _SNICallback: null,
      servername: 'api.smartthings.com',
      alpnProtocol: false,
      authorized: true,
      authorizationError: null,
      encrypted: true,
      _events: [Object: null prototype],
      _eventsCount: 9,
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'api.smartthings.com',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _writableState: [WritableState],
      allowHalfOpen: false,
      _maxListeners: undefined,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: undefined,
      _server: null,
      ssl: [TLSWrap],
      _requestCert: true,
      _rejectUnauthorized: true,
      timeout: 5000,
      parser: null,
      _httpMessage: null,
      autoSelectFamilyAttemptedAddresses: [Array],
      Symbol(alpncallback): null,
      Symbol(res): [TLSWrap],
      Symbol(verified): true,
      Symbol(pendingSession): null,
      Symbol(async_id_symbol): -1,
      Symbol(kHandle): [TLSWrap],
      Symbol(lastWriteQueueSize): 0,
      Symbol(timeout): Timeout {
        _idleTimeout: 5000,
        _idlePrev: [TimersList],
        _idleNext: [TimersList],
        _idleStart: 6310,
        _onTimeout: [Function: bound ],
        _timerArgs: undefined,
        _repeat: null,
        _destroyed: false,
        Symbol(refed): false,
        Symbol(kHasPrimitive): false,
        Symbol(asyncId): 135,
        Symbol(triggerId): 133,
        Symbol(kAsyncContextFrame): undefined
      },
      Symbol(kBuffer): null,
      Symbol(kBufferCb): null,
      Symbol(kBufferGen): null,
      Symbol(shapeMode): true,
      Symbol(kCapture): false,
      Symbol(kSetNoDelay): false,
      Symbol(kSetKeepAlive): true,
      Symbol(kSetKeepAliveInitialDelay): 1,
      Symbol(kBytesRead): 0,
      Symbol(kBytesWritten): 0,
      Symbol(connect-options): [Object]
    },
    Symbol(kOutHeaders): [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'user-agent': [Array],
      'accept-language': [Array],
      authorization: [Array],
      'content-length': [Array],
      'accept-encoding': [Array],
      host: [Array]
    },
    Symbol(errored): null,
    Symbol(kHighWaterMark): 16384,
    Symbol(kRejectNonStandardBodyWrites): false,
    Symbol(kUniqueHeaders): null
  },
  response: {
    status: 403,
    statusText: 'Forbidden',
    headers: Object [AxiosHeaders] {
      date: 'Sun, 29 Mar 2026 09:22:04 GMT',
      'content-type': 'application/json',
      'transfer-encoding': 'chunked',
      connection: 'keep-alive',
      server: 'openresty',
      'x-ratelimit-limit': '50',
      'x-ratelimit-remaining': '50',
      'x-ratelimit-reset': '55348',
      'access-control-allow-origin': '*'
    },
    config: {
      transitional: [Object],
      adapter: [Array],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      env: [Object],
      validateStatus: [Function: validateStatus],
      headers: [Object [AxiosHeaders]],
      url: 'https://api.smartthings.com/rules',
      method: 'post',
      params: [Object],
      data: '{"name":"[R] Auto-off: Bedroom: Fan","actions":[{"if":{"remains":{"equals":{"left":{"device":{"devices":["12345678-9ABC-DEF0-1234-45689ABC"],"component":"main","capability":"switch","attribute":"switch"}},"right":{"string":"on"}},"duration":{"value":{"integer":45},"unit":"Minute"}},"then":[{"command":{"devices":["12345678-9ABC-DEF0-1234-45689ABC"],"commands":[{"component":"main","capability":"switch","command":"off"}]}}]}}]}',
      paramsSerializer: [Object],
      allowAbsoluteUrls: true
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: true,
      _last: false,
      chunkedEncoding: false,
      shouldKeepAlive: true,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 428,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: true,
      _header: 'POST /rules?locationId=REDACTED HTTP/1.1\r\n' +
        'Accept: application/json\r\n' +
        'Content-Type: application/json;charset=utf-8\r\n' +
        'User-Agent: @smartthings/cli\r\n' +
        'Accept-Language: en-GB\r\n' +
        'Authorization: Bearer REDACTED\r\n' +
        'Content-Length: 428\r\n' +
        'Accept-Encoding: gzip, compress, deflate, br\r\n' +
        'Host: api.smartthings.com\r\n' +
        'Connection: keep-alive\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/rules?locationId=REDACTED',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'api.smartthings.com',
      protocol: 'https:',
      _redirectable: [Writable],
      Symbol(shapeMode): false,
      Symbol(kCapture): false,
      Symbol(kBytesWritten): 0,
      Symbol(kNeedDrain): false,
      Symbol(corked): 0,
      Symbol(kChunkedBuffer): [],
      Symbol(kChunkedLength): 0,
      Symbol(kSocket): [TLSSocket],
      Symbol(kOutHeaders): [Object: null prototype],
      Symbol(errored): null,
      Symbol(kHighWaterMark): 16384,
      Symbol(kRejectNonStandardBodyWrites): false,
      Symbol(kUniqueHeaders): null
    },
    data: { requestId: '69c8ef3c0000000041b897545ef657c2', error: [Object] }
  },
  status: 403
}

>

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions