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 nil

View on GitHub (pinned to 7a8683dc7f)

Solutions

  1. Verify the script exists under the configured dir with the exact name in the URL (`ls <dir>`), respecting case and extensions
  2. Fix the client URL's path to match a real script file
  3. 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

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.