louislam/dockge · error · ValidationError

Service name must be a string.

Error message

Service name must be a string.

What it means

Thrown as a ValidationError in the interactiveTerminal handler when the serviceName argument is not a string. Like the other checks in this handler it validates raw socket input before resolving the stack service that will host the shell.

Source

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

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

View on GitHub (pinned to f809ae192b)

Solutions

  1. Pass the service name as a string, e.g. socket.emit('interactiveTerminal', stackName, service.name, shell, cb).
  2. Ensure a service is selected/available in the UI before opening the terminal.
  3. Check stack definition includes services and the client receives them correctly.
  4. Coerce only after verifying the value is defined (String(serviceName)).

Example fix

// before
socket.emit("interactiveTerminal", stackName, undefined, shell, cb);
// after
const serviceName = selectedService?.name;
if (typeof serviceName === "string") {
    socket.emit("interactiveTerminal", stackName, serviceName, shell, cb);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof serviceName !== "string" || serviceName === "") { showError("Select a service"); return; }
socket.emit("interactiveTerminal", stackName, serviceName, shell, cb);

Type guard

const isNonEmptyString = (v) => typeof v === "string" && v.length > 0;

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 second argument (serviceName) that is null, undefined, a number, or an object instead of a string.

Common situations: Service dropdown empty (no services in the stack), client passing a service object rather than its name, or a UI bug where no service is chosen before opening the terminal.

Related errors


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