louislam/dockge · error · ValidationError

Shell must be a string.

Error message

Shell must be a string.

What it means

Thrown as a ValidationError in the interactiveTerminal handler when the shell argument is not a string. The handler requires an explicit shell path/name (e.g. "/bin/sh", "/bin/bash") to exec inside the container.

Source

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

                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({
                    ok: true,
                }, callback);
            } catch (e) {
                callbackError(e, callback);
            }
        });

        // Join Output Terminal

View on GitHub (pinned to f809ae192b)

Solutions

  1. Always pass an explicit shell string, e.g. socket.emit('interactiveTerminal', stackName, serviceName, '/bin/sh', cb).
  2. Default the client-side shell to '/bin/sh' when no user preference exists.
  3. Persist/restore the shell preference before opening terminals.
  4. Validate the value is a non-empty string before emitting.

Example fix

// before
socket.emit("interactiveTerminal", stackName, serviceName, settings.shell, cb);
// after
const shell = typeof settings.shell === "string" ? settings.shell : "/bin/sh";
socket.emit("interactiveTerminal", stackName, serviceName, shell, cb);
Defensive patterns

Strategy: fallback

Validate before calling

const safeShell = typeof shell === "string" && shell ? shell : "/bin/sh";
socket.emit("interactiveTerminal", stackName, serviceName, safeShell, cb);

Type guard

const isShell = (v) => typeof v === "string" && v.startsWith("/") || v === "sh" || v === "bash";

Try / catch

socket.emit("interactiveTerminal", stackName, serviceName, shell, (res) => { if (res?.error) { retryWithShell("/bin/sh"); } });

Prevention

When it happens

Trigger: Emitting 'interactiveTerminal' with a third argument (shell) that is null, undefined, a number, or an object instead of a string.

Common situations: Shell preference not yet saved/selected in client settings, config lookup returning undefined for the default shell, or omitting the argument assuming a server default (there is none).

Related errors


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