{"record":{"id":"42b1eb1edebe368e","repo":"louislam/dockge","slug":"command-must-be-a-number","errorCode":null,"errorMessage":"Command must be a number.","messagePattern":"Command must be a number\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"backend/agent-socket-handlers/terminal-socket-handler.ts","lineNumber":178,"sourceCode":"                callbackResult({\n                    ok: true,\n                }, callback);\n            } catch (e) {\n                callbackError(e, callback);\n            }\n        });\n\n        // Resize Terminal\n        agentSocket.on(\"terminalResize\", async (terminalName: unknown, rows: unknown, cols: unknown) => {\n            log.info(\"terminalResize\", `Terminal: ${terminalName}`);\n            try {\n                checkLogin(socket);\n                if (typeof terminalName !== \"string\") {\n                    throw new Error(\"Terminal name must be a string.\");\n                }\n\n                if (typeof rows !== \"number\") {\n                    throw new Error(\"Command must be a number.\");\n                }\n                if (typeof cols !== \"number\") {\n                    throw new Error(\"Command must be a number.\");\n                }\n\n                let terminal = Terminal.getTerminal(terminalName);\n\n                // log.info(\"terminal\", terminal);\n                if (terminal instanceof Terminal) {\n                    //log.debug(\"terminalInput\", \"Terminal found, writing to terminal.\");\n                    terminal.rows = rows;\n                    terminal.cols = cols;\n                } else {\n                    throw new Error(`${terminalName} Terminal not found.`);\n                }\n            } catch (e) {\n                log.debug(\"terminalResize\",\n                        // Added to prevent the lint error when adding the type","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/louislam/dockge/blob/f809ae192b571944ad773e9866d3e67064ae8043/backend/agent-socket-handlers/terminal-socket-handler.ts#L160-L196","documentation":"The terminalResize socket handler validates every argument type before touching the terminal. The `rows` parameter arrived as something other than a number (string, null, undefined, object...), so the handler throws immediately instead of writing a bad value to the terminal. The copy-pasted message says 'Command' but this branch is really about the rows dimension.","triggerScenarios":"Emitting socket event `terminalResize` with a non-number second argument, e.g. socket.emit('terminalResize', 'stack1', '80', 24) passing rows as a string, or omitting it so rows === undefined.","commonSituations":"Frontend code passing xterm dimensions from DOM strings or form inputs without Number() conversion; stale clients predating the rows/cols signature; agents forwarding JSON where numbers were serialized as strings.","solutions":["Pass rows as a JavaScript number: socket.emit('terminalResize', terminalName, Number(rows), cols)","On the client, coerce/validate before emitting: if (typeof rows !== 'number') throw new TypeError('rows must be a number')","Check the emitting code actually passes rows and cols positionally in the right order"],"exampleFix":"// before\nsocket.emit(\"terminalResize\", terminalName, terminal.rows.toString(), terminal.cols);\n// after\nsocket.emit(\"terminalResize\", terminalName, parseInt(rowsInput, 10), parseInt(colsInput, 10));","handlingStrategy":"type-guard","validationCode":"function isValidResize(p) { return typeof p.terminalName === \"string\" && typeof p.rows === \"number\" && Number.isFinite(p.rows) && typeof p.cols === \"number\" && Number.isFinite(p.cols); }\nif (!isValidResize(payload)) throw new TypeError(\"terminalResize requires (string, number, number)\");\nsocket.emit(\"terminalResize\", payload.terminalName, payload.rows, payload.cols);","typeGuard":"function isNumber(v: unknown): v is number { return typeof v === \"number\" && Number.isFinite(v); }","tryCatchPattern":"try { socket.emit(\"terminalResize\", name, rows, cols); } catch (e) { if (e instanceof TypeError) { /* coerce and retry: Number(rows) */ } else { throw e; } }","preventionTips":["Coerce DOM-derived dimensions with Number()/parseInt before emitting","Keep a shared TS type for the terminalResize payload used by both client and server","Validate payloads at the socket wrapper layer once, not per handler"],"tags":["validation","typescript","socket-io","terminal"],"backgroundTag":"invalid-argument-type","analyzedSha":"f809ae192b571944ad773e9866d3e67064ae8043","analyzedAt":"2026-08-31T19:13:50.919Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}