louislam/dockge · error · ValidationError

Stack name must be a string.

Error message

Stack name must be a string.

What it means

Thrown as a ValidationError in the interactiveTerminal handler when the stackName argument is not a string. The handler validates all raw socket arguments before using them to look up a Docker/Compose stack. It prevents type-confusion when the client sends malformed data.

Source

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

        // Check if MainTerminal is enabled
        agentSocket.on("checkMainTerminal", async (callback) => {
            try {
                checkLogin(socket);
                callbackResult({
                    ok: server.config.enableConsole,
                }, callback);
            } catch (e) {
                callbackError(e, callback);
            }
        });

        // Interactive Terminal for containers
        agentSocket.on("interactiveTerminal", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {
            try {
                checkLogin(socket);

                if (typeof(stackName) !== "string") {
                    throw new ValidationError("Stack name must be a string.");
                }

                if (typeof(serviceName) !== "string") {
                    throw new ValidationError("Service name must be a string.");
                }

                if (typeof(shell) !== "string") {
                    throw new ValidationError("Shell must be a string.");
                }

                log.debug("interactiveTerminal", "Stack name: " + stackName);
                log.debug("interactiveTerminal", "Service name: " + serviceName);

                // Get stack
                const stack = await Stack.getStack(server, stackName);
                stack.joinContainerTerminal(socket, serviceName, shell);

                callbackResult({

View on GitHub (pinned to f809ae192b)

Solutions

  1. Pass the stack's name string, not the stack object: socket.emit('interactiveTerminal', stack.name, serviceName, shell, cb).
  2. Verify the stack list has loaded and a stack is selected before opening a terminal.
  3. Stringify/coerce deliberately (String(stackName)) after confirming it is defined.
  4. Handle the callback error to show the user which argument was invalid.

Example fix

// before
socket.emit("interactiveTerminal", stack, serviceName, "/bin/sh", cb);
// after
socket.emit("interactiveTerminal", stack.name, serviceName, "/bin/sh", cb);
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof stack?.name !== "string") { showError("Select a stack first"); return; }
socket.emit("interactiveTerminal", stack.name, serviceName, shell, cb);

Type guard

const isStackRef = (s) => typeof s === "string" || (s && typeof s.name === "string");

Try / catch

socket.emit("interactiveTerminal", stackName, serviceName, shell, (res) => { if (res?.error) showError(res.error.message); });

Prevention

When it happens

Trigger: Emitting 'interactiveTerminal' with a first argument that is not a string (null, undefined, number, object) while the remaining args (serviceName, shell) would otherwise be valid.

Common situations: Client select/dropdown bound to a stack object instead of its name string; stack list not yet loaded so the value is undefined; JSON round-tripping changing types.

Related errors


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