-
Notifications
You must be signed in to change notification settings - Fork 130
Unhelpful diagnostics when creating or updating Rules #829
Description
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.
- An incorrect device ID.
- 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
}
>