louislam/dockge · error · Error

Command must be a string.

Error message

Command must be a string.

What it means

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.

Source

Thrown at backend/agent-socket-handlers/terminal-socket-handler.ts:21

import { log } from "../log";
import { InteractiveTerminal, MainTerminal, Terminal } from "../terminal";
import { Stack } from "../stack";
import { AgentSocketHandler } from "../agent-socket-handler";
import { AgentSocket } from "../../common/agent-socket";

export class TerminalSocketHandler extends AgentSocketHandler {
    create(socket : DockgeSocket, server : DockgeServer, agentSocket : AgentSocket) {

        agentSocket.on("terminalInput", async (terminalName : unknown, cmd : unknown, callback) => {
            try {
                checkLogin(socket);

                if (typeof(terminalName) !== "string") {
                    throw new Error("Terminal name must be a string.");
                }

                if (typeof(cmd) !== "string") {
                    throw new Error("Command must be a string.");
                }

                let terminal = Terminal.getTerminal(terminalName);
                if (terminal instanceof InteractiveTerminal) {
                    //log.debug("terminalInput", "Terminal found, writing to terminal.");
                    terminal.write(cmd);
                } else {
                    throw new Error("Terminal not found or it is not a Interactive Terminal.");
                }
            } catch (e) {
                callbackError(e, callback);
            }
        });

        // Main Terminal
        agentSocket.on("mainTerminal", async (terminalName : unknown, callback) => {
            try {
                checkLogin(socket);

View on GitHub (pinned to f809ae192b)

Solutions

  1. Ensure the client passes the command as a plain string when emitting 'terminalInput' (String(cmd) if needed).
  2. Check client code for undefined/null command variables and handle the empty case before emitting.
  3. Log the payload on the client right before emit to confirm its type.
  4. Wrap the emit/callback in error handling and surface callbackError to the UI.

Example fix

// before
socket.emit("terminalInput", terminalName, cmd);
// after
if (typeof cmd === "string") {
    socket.emit("terminalInput", terminalName, cmd);
} else {
    console.error("terminalInput requires a string command, got:", typeof cmd);
}
Defensive patterns

Strategy: type-guard

Validate before calling

function assertString(v) { if (typeof v !== "string") throw new TypeError("cmd must be a string, got " + typeof v); }
assertString(cmd); socket.emit("terminalInput", terminalName, cmd);

Type guard

const isString = (v) => typeof v === "string";

Try / catch

socket.emit("terminalInput", terminalName, cmd, (res) => { if (res?.error) console.error("terminalInput failed:", res.error.message); });

Prevention

When it happens

Trigger: 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)).

Common situations: 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.

Related errors


AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31). Data as JSON: /api/errors/c82acb50347afa75. Report an issue: GitHub.