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
- Pass the stack's name string, not the stack object: socket.emit('interactiveTerminal', stack.name, serviceName, shell, cb).
- Verify the stack list has loaded and a stack is selected before opening a terminal.
- Stringify/coerce deliberately (String(stackName)) after confirming it is defined.
- 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
- Pass stack.name (string), never the stack object.
- Disable the terminal button until a stack is selected and loaded.
- Validate all three string args before emitting.
- Use a typed emit helper for stack-related events.
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
- Service name must be a string.
- Shell 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/6d1909f8042277e3.
Report an issue: GitHub.