{"record":{"id":"c82acb50347afa75","repo":"louislam/dockge","slug":"command-must-be-a-string","errorCode":null,"errorMessage":"Command must be a string.","messagePattern":"Command must be a string\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"backend/agent-socket-handlers/terminal-socket-handler.ts","lineNumber":21,"sourceCode":"import { log } from \"../log\";\nimport { InteractiveTerminal, MainTerminal, Terminal } from \"../terminal\";\nimport { Stack } from \"../stack\";\nimport { AgentSocketHandler } from \"../agent-socket-handler\";\nimport { AgentSocket } from \"../../common/agent-socket\";\n\nexport class TerminalSocketHandler extends AgentSocketHandler {\n    create(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket) {\n\n        agentSocket.on(\"terminalInput\", async (terminalName : unknown, cmd : unknown, callback) => {\n            try {\n                checkLogin(socket);\n\n                if (typeof(terminalName) !== \"string\") {\n                    throw new Error(\"Terminal name must be a string.\");\n                }\n\n                if (typeof(cmd) !== \"string\") {\n                    throw new Error(\"Command must be a string.\");\n                }\n\n                let terminal = Terminal.getTerminal(terminalName);\n                if (terminal instanceof InteractiveTerminal) {\n                    //log.debug(\"terminalInput\", \"Terminal found, writing to terminal.\");\n                    terminal.write(cmd);\n                } else {\n                    throw new Error(\"Terminal not found or it is not a Interactive Terminal.\");\n                }\n            } catch (e) {\n                callbackError(e, callback);\n            }\n        });\n\n        // Main Terminal\n        agentSocket.on(\"mainTerminal\", async (terminalName : unknown, callback) => {\n            try {\n                checkLogin(socket);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/louislam/dockge/blob/f809ae192b571944ad773e9866d3e67064ae8043/backend/agent-socket-handlers/terminal-socket-handler.ts#L3-L39","documentation":"Thrown inside the terminalInput socket handler (registered in create()) when the cmd argument sent by the client is not a string. The handler validates raw socket payloads because anything can be sent over the wire, so it explicitly checks typeof before writing to a terminal. It is a defensive input-validation error, not an internal failure.","triggerScenarios":"Emitting the 'terminalInput' socket event with a command argument that is null, undefined, a number, an object, or an array instead of a string (e.g. terminalSocket.emit('terminalInput', name, 42)).","commonSituations":"Client-side state not yet initialized (undefined command), JSON payload deserialization surprises, UI sending keystroke objects instead of strings, or a refactored client API passing structured command objects.","solutions":["Ensure the client passes the command as a plain string when emitting 'terminalInput' (String(cmd) if needed).","Check client code for undefined/null command variables and handle the empty case before emitting.","Log the payload on the client right before emit to confirm its type.","Wrap the emit/callback in error handling and surface callbackError to the UI."],"exampleFix":"// before\nsocket.emit(\"terminalInput\", terminalName, cmd);\n// after\nif (typeof cmd === \"string\") {\n    socket.emit(\"terminalInput\", terminalName, cmd);\n} else {\n    console.error(\"terminalInput requires a string command, got:\", typeof cmd);\n}","handlingStrategy":"type-guard","validationCode":"function assertString(v) { if (typeof v !== \"string\") throw new TypeError(\"cmd must be a string, got \" + typeof v); }\nassertString(cmd); socket.emit(\"terminalInput\", terminalName, cmd);","typeGuard":"const isString = (v) => typeof v === \"string\";","tryCatchPattern":"socket.emit(\"terminalInput\", terminalName, cmd, (res) => { if (res?.error) console.error(\"terminalInput failed:\", res.error.message); });","preventionTips":["Always pass commands as strings; serialize structured commands client-side before emitting.","Lint for emit calls with non-literal arguments and add typeof assertions.","Log payloads during development to catch undefined values early.","Centralize socket emits in a typed wrapper that validates arguments."],"tags":["validation","typescript","socket-io","terminal"],"backgroundTag":"invalid-socket-payload-type","analyzedSha":"f809ae192b571944ad773e9866d3e67064ae8043","analyzedAt":"2026-08-31T19:13:50.919Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}