{"record":{"id":"8c37ca7b4d1f5bd5","repo":"louislam/dockge","slug":"shell-must-be-a-string","errorCode":null,"errorMessage":"Shell must be a string.","messagePattern":"Shell must be a string\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"backend/agent-socket-handlers/terminal-socket-handler.ts","lineNumber":100,"sourceCode":"                callbackError(e, callback);\n            }\n        });\n\n        // Interactive Terminal for containers\n        agentSocket.on(\"interactiveTerminal\", async (stackName : unknown, serviceName : unknown, shell : unknown, callback) => {\n            try {\n                checkLogin(socket);\n\n                if (typeof(stackName) !== \"string\") {\n                    throw new ValidationError(\"Stack name must be a string.\");\n                }\n\n                if (typeof(serviceName) !== \"string\") {\n                    throw new ValidationError(\"Service name must be a string.\");\n                }\n\n                if (typeof(shell) !== \"string\") {\n                    throw new ValidationError(\"Shell must be a string.\");\n                }\n\n                log.debug(\"interactiveTerminal\", \"Stack name: \" + stackName);\n                log.debug(\"interactiveTerminal\", \"Service name: \" + serviceName);\n\n                // Get stack\n                const stack = await Stack.getStack(server, stackName);\n                stack.joinContainerTerminal(socket, serviceName, shell);\n\n                callbackResult({\n                    ok: true,\n                }, callback);\n            } catch (e) {\n                callbackError(e, callback);\n            }\n        });\n\n        // Join Output Terminal","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/louislam/dockge/blob/f809ae192b571944ad773e9866d3e67064ae8043/backend/agent-socket-handlers/terminal-socket-handler.ts#L82-L118","documentation":"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.","triggerScenarios":"Emitting 'interactiveTerminal' with a third argument (shell) that is null, undefined, a number, or an object instead of a string.","commonSituations":"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).","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."],"exampleFix":"// before\nsocket.emit(\"interactiveTerminal\", stackName, serviceName, settings.shell, cb);\n// after\nconst shell = typeof settings.shell === \"string\" ? settings.shell : \"/bin/sh\";\nsocket.emit(\"interactiveTerminal\", stackName, serviceName, shell, cb);","handlingStrategy":"fallback","validationCode":"const safeShell = typeof shell === \"string\" && shell ? shell : \"/bin/sh\";\nsocket.emit(\"interactiveTerminal\", stackName, serviceName, safeShell, cb);","typeGuard":"const isShell = (v) => typeof v === \"string\" && v.startsWith(\"/\") || v === \"sh\" || v === \"bash\";","tryCatchPattern":"socket.emit(\"interactiveTerminal\", stackName, serviceName, shell, (res) => { if (res?.error) { retryWithShell(\"/bin/sh\"); } });","preventionTips":["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."],"tags":["validation","typescript","socket-io","docker","terminal"],"backgroundTag":"invalid-socket-payload-type","analyzedSha":"f809ae192b571944ad773e9866d3e67064ae8043","analyzedAt":"2026-08-31T19:13:50.919Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}