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 TerminalView on GitHub (pinned to f809ae192b)
Solutions
- Always pass an explicit shell string, e.g. socket.emit('interactiveTerminal', stackName, serviceName, '/bin/sh', cb).
- Default the client-side shell to '/bin/sh' when no user preference exists.
- Persist/restore the shell preference before opening terminals.
- 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
- Never emit an unset shell; default to "/bin/sh".
- Persist the shell preference with a string default.
- Validate settings loaded from storage before use.
- Document that the server requires an explicit shell string.
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
- Stack name must be a string.
- Service name must be a string.
- Command must be a string.
- Command must be a number.
- Stack name and service name must be strings
AI-assisted analysis of louislam/dockge@f809ae192b (2026-08-31).
Data as JSON: /api/errors/8c37ca7b4d1f5bd5.
Report an issue: GitHub.