{"record":{"id":"482d032f2c446792","repo":"joewalnes/websocketd","slug":"500-internal-server-error","errorCode":null,"errorMessage":"500 Internal Server Error","messagePattern":"500 Internal Server Error","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"libwebsocketd/http.go","lineNumber":132,"sourceCode":"\tif !isWebSocketUpgrade(req) {\n\t\treturn false\n\t}\n\n\tif h.noteForkCreated() != nil {\n\t\tlog.Error(\"http\", \"Max of possible forks already active, upgrade rejected\")\n\t\thttp.Error(w, \"429 Too Many Requests\", http.StatusTooManyRequests)\n\t\treturn true\n\t}\n\tdefer h.noteForkCompleted()\n\n\thandler, err := NewWebsocketdHandler(h, req, log)\n\tif err != nil {\n\t\tif err == ErrScriptNotFound {\n\t\t\tlog.Access(\"session\", \"NOT FOUND: %s\", err)\n\t\t\thttp.Error(w, \"404 Not Found\", 404)\n\t\t} else {\n\t\t\tlog.Access(\"session\", \"INTERNAL ERROR: %s\", err)\n\t\t\thttp.Error(w, \"500 Internal Server Error\", 500)\n\t\t}\n\t\treturn true\n\t}\n\n\tvar headers http.Header\n\tif len(h.Config.Headers)+len(h.Config.HeadersWs) > 0 {\n\t\theaders = http.Header(make(map[string][]string))\n\t\tpushHeaders(headers, h.Config.Headers)\n\t\tpushHeaders(headers, h.Config.HeadersWs)\n\t}\n\n\tupgrader := &websocket.Upgrader{\n\t\tHandshakeTimeout: h.Config.HandshakeTimeout,\n\t\tCheckOrigin: func(r *http.Request) bool {\n\t\t\treturn checkOrigin(req, h.Config, log) == nil\n\t\t},\n\t}\n\tconn, err := upgrader.Upgrade(w, req, headers)","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/joewalnes/websocketd/blob/7a8683dc7f9778dc615945aaed2a8dc77290227b/libwebsocketd/http.go#L114-L150","documentation":"This is the generic catch-all response sent by serveWebSocket (libwebsocketd/http.go:110) when NewWebsocketdHandler returns an error that is NOT ErrScriptNotFound. The server logs 'INTERNAL ERROR' with the underlying cause at access-log level and writes an HTTP 500 with the plain body '500 Internal Server Error'. It means the WebSocket upgrade request could not be turned into a handler for a reason other than 'the script does not exist' — e.g. the script path was found but is not executable, permission was denied, or command configuration is invalid. The specific cause is only visible in the server log, not in the response body.","triggerScenarios":"A WebSocket upgrade request hits a server configured with --command or --scriptdir, noteForkCreated succeeds, but NewWebsocketdHandler fails with an error other than ErrScriptNotFound: e.g. the resolved script file exists but lacks the executable bit (permission denied), the script path is a directory, the configured --command binary cannot be found/executed by the OS, or constructing the handler from a malformed URL/request path fails in an unexpected way.","commonSituations":"Deploying a script without 'chmod +x'; uploading a script owned by a user the websocketd process cannot execute; pointing --command at a binary that is absent at runtime (PATH differs under systemd/containers); a scriptdir entry that is a directory rather than a file; SELinux/AppArmor denying exec; typos in the script path that resolve to something existing but invalid.","solutions":["Check the websocketd server log for the 'session INTERNAL ERROR: <err>' line — it contains the real underlying error (e.g. permission denied).","Make the script executable: chmod +x /path/to/script, and verify ownership/readability by the user running websocketd.","Verify the --command or scriptdir entry actually points to an executable file (not a directory) reachable under the server's PATH.","Test the script locally the way websocketd runs it (e.g. run it manually reading stdin/writing stdout) to rule out interpreter/shebang problems.","If the file truly does not exist, expect a 404 instead — a 500 here means the failure mode is environmental (permissions, exec, config)."],"exampleFix":"// before: script present but not executable -> 500\n$ ls -l handler.sh\n-rw-r--r-- 1 user user handler.sh\n\n// after: make it executable\n$ chmod +x handler.sh\n$ websocketd --port=8080 ./handler.sh","handlingStrategy":"validation","validationCode":"// Before pointing websocketd at the script, verify it is an executable regular file\nfunc scriptRunnable(path string) error {\n    fi, err := os.Stat(path)\n    if err != nil {\n        return fmt.Errorf(\"script missing: %w\", err)\n    }\n    if fi.IsDir() {\n        return fmt.Errorf(\"%s is a directory, not a script\", path)\n    }\n    if fi.Mode()&0o111 == 0 {\n        return fmt.Errorf(\"%s is not executable (chmod +x)\", path)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"// 500 carries no detail; correlate with server logs. Watch the access log for the real cause:\n// grep 'session INTERNAL ERROR' websocketd.log\n// On the client, treat any non-101 WebSocket handshake response as fatal:\nconn, resp, err := websocket.DefaultDialer.Dial(wsURL, nil)\nif err != nil {\n    if resp != nil && resp.StatusCode == http.StatusInternalServerError {\n        log.Fatalf(\"server failed to start script (500); check server logs: %v\", err)\n    }\n    return err\n}","preventionTips":["Always chmod +x scripts before deployment and verify with ls -l or a CI check.","Smoke-test the exact --command/scriptdir path with the same user websocketd runs as (systemd User=, container USER).","Keep script interpreter shebangs absolute and present in the deployment image.","Monitor server logs for 'session INTERNAL ERROR' lines and alert on them."],"tags":["http","http-500","websocket","permissions","server-error"],"backgroundTag":"http-500-internal-server-error","analyzedSha":"7a8683dc7f9778dc615945aaed2a8dc77290227b","analyzedAt":"2026-09-03T13:52:22.309Z","contentChangedAt":"2026-09-03T13:52:22.309Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}