joewalnes/websocketd · error
could not resolve script for path %q
Error message
could not resolve script for path %q
What it means
GetURLInfo maps an incoming WebSocket/HTTP URL path to a script under the configured base directory (or an explicit script path). When every resolution strategy fails to produce an existing script for the requested path, it returns this error, which the handler turns into a failed request rather than a process spawn.
Source
Thrown at libwebsocketd/handler.go:183
}
// Verify the resolved path stays within the script directory.
// This prevents symlink attacks where a link inside ScriptDir
// points to an arbitrary file outside it.
if err := checkPathBoundary(urlInfo.FilePath, config.ScriptDir); err != nil {
return nil, ErrScriptNotFound
}
// no extra args
if isLastPart {
return urlInfo, nil
}
// build path info from extra parts of url
urlInfo.PathInfo = "/" + strings.Join(parts[i+1:], "/")
return urlInfo, nil
}
return nil, fmt.Errorf("could not resolve script for path %q", path)
}
// checkPathBoundary resolves symlinks and verifies the real path is within the
// allowed directory. Returns an error if the path escapes the boundary.
func checkPathBoundary(path, boundary string) error {
realPath, err := filepath.EvalSymlinks(path)
if err != nil {
return err
}
realBoundary, err := filepath.EvalSymlinks(boundary)
if err != nil {
return err
}
// Ensure the resolved path starts with the resolved boundary
if !strings.HasPrefix(realPath, realBoundary+string(filepath.Separator)) && realPath != realBoundary {
return fmt.Errorf("path %q escapes boundary %q", realPath, realBoundary)
}
return nilView on GitHub (pinned to 7a8683dc7f)
Solutions
- Verify the script exists under the configured dir with the exact name in the URL (`ls <dir>`), respecting case and extensions
- Fix the client URL's path to match a real script file
- Check you launched with the intended --dir (or explicit script) so resolution searches the right directory
Example fix
// client
// before
new WebSocket('ws://host:8080/echos/')
// after
new WebSocket('ws://host:8080/echo/') // scripts/echo exists on disk Defensive patterns
Strategy: try-catch
Validate before calling
// client side: confirm the script exists for the URL path
const script = 'echo'
const res = await fetch(`http://host:8080/${script}/`, {method:'HEAD'})
if (!res.ok) throw new Error(`no script resolved for /${script}/`) Try / catch
try {
const ws = new WebSocket('ws://host:8080/echo/')
} catch (e) {
// 'could not resolve script for path' — check --dir contents and URL casing/extensions
} Prevention
- Keep URL first segment equal to the script filename on disk (mind case sensitivity)
- List the --dir contents and compare with client endpoints during deploys
- Include file extensions the resolver expects
- Add a smoke test that opens each expected endpoint after deploy
When it happens
Trigger: Requesting /echo/x when baseDir only contains count.sh; URL path with extra segments mapping to a nonexistent file; --dir mode where the client URL's first segment names no file in the directory; requesting an empty/no path where nothing can be resolved.
Common situations: Client built with a wrong endpoint URL after renaming scripts; case-sensitivity mismatch on Linux (Echo.sh vs echo.sh); missing file extension the resolver expects; deploying scripts to a different directory than the one passed to --dir.
Related errors
AI-assisted analysis of joewalnes/websocketd@7a8683dc7f (2026-09-03).
Data as JSON: /api/errors/24696366d5ea7ecd.
Report an issue: GitHub.