louislam/dockge · error · Error
Command must be a number.
Error message
Command must be a number.
What it means
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.
Source
Thrown at backend/agent-socket-handlers/terminal-socket-handler.ts:178
callbackResult({
ok: true,
}, callback);
} catch (e) {
callbackError(e, callback);
}
});
// Resize Terminal
agentSocket.on("terminalResize", async (terminalName: unknown, rows: unknown, cols: unknown) => {
log.info("terminalResize", `Terminal: ${terminalName}`);
try {
checkLogin(socket);
if (typeof terminalName !== "string") {
throw new Error("Terminal name must be a string.");
}
if (typeof rows !== "number") {
throw new Error("Command must be a number.");
}
if (typeof cols !== "number") {
throw new Error("Command must be a number.");
}
let terminal = Terminal.getTerminal(terminalName);
// log.info("terminal", terminal);
if (terminal instanceof Terminal) {
//log.debug("terminalInput", "Terminal found, writing to terminal.");
terminal.rows = rows;
terminal.cols = cols;
} else {
throw new Error(`${terminalName} Terminal not found.`);
}
} catch (e) {
log.debug("terminalResize",
// Added to prevent the lint error when adding the typeView on GitHub (pinned to f809ae192b)
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
Example fix
// before
socket.emit("terminalResize", terminalName, terminal.rows.toString(), terminal.cols);
// after
socket.emit("terminalResize", terminalName, parseInt(rowsInput, 10), parseInt(colsInput, 10)); Defensive patterns
Strategy: type-guard
Validate before calling
function isValidResize(p) { return typeof p.terminalName === "string" && typeof p.rows === "number" && Number.isFinite(p.rows) && typeof p.cols === "number" && Number.isFinite(p.cols); }
if (!isValidResize(payload)) throw new TypeError("terminalResize requires (string, number, number)");
socket.emit("terminalResize", payload.terminalName, payload.rows, payload.cols); Type guard
function isNumber(v: unknown): v is number { return typeof v === "number" && Number.isFinite(v); } Try / catch
try { socket.emit("terminalResize", name, rows, cols); } catch (e) { if (e instanceof TypeError) { /* coerce and retry: Number(rows) */ } else { throw e; } } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- Command must be a string.
- Stack name must be a string.
- Service name must be a string.
- Shell must be a string.
- Stack name and service name must be strings
AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31).
Data as JSON: /api/errors/42b1eb1edebe368e.
Report an issue: GitHub.