paperclipai/paperclip · warning
Invalid file path
Error message
Invalid file path
What it means
Returned as HTTP 400 by GET /_plugins/:pluginId/ui/* (server/src/routes/plugin-ui-static.ts:324) when the dev-proxy branch is active (a devUiUrl is configured in company plugin config and NODE_ENV is not production) and the wildcard file path cannot be percent-decoded. decodeURIComponent throws on malformed escape sequences such as a lone '%' or '%zz', and the route converts that into a 400.
Source
Thrown at server/src/routes/plugin-ui-static.ts:324
// Dev proxy is only available in development mode
if (process.env.NODE_ENV === "production") {
log.warn(
{ pluginId: plugin.id },
"plugin-ui-static: devUiUrl ignored in production",
);
// Fall through to static file serving below
} else {
// Guard against rawFilePath overriding the base URL via protocol
// scheme (e.g. "https://evil.com/x") or protocol-relative paths
// (e.g. "//evil.com/x") which cause `new URL(path, base)` to
// ignore the base entirely.
// Normalize percent-encoding so encoded slashes (%2F) can't bypass
// the protocol/path checks below.
let decodedPath: string;
try {
decodedPath = decodeURIComponent(rawFilePath);
} catch {
res.status(400).json({ error: "Invalid file path" });
return;
}
if (
decodedPath.includes("://") ||
decodedPath.startsWith("//") ||
decodedPath.startsWith("\\\\")
) {
res.status(400).json({ error: "Invalid file path" });
return;
}
// Proxy the request to the dev server
const targetUrl = new URL(rawFilePath, devUiUrl.endsWith("/") ? devUiUrl : devUiUrl + "/");
// SSRF protection: only allow http/https and localhost targets for dev proxy
if (targetUrl.protocol !== "http:" && targetUrl.protocol !== "https:") {
res.status(400).json({ error: "devUiUrl must use http or https protocol" });
return;View on GitHub (pinned to 120ae5428f)
Solutions
- Fix the requesting URL so it is a valid percent-encoded path (a literal '%' must be sent as '%25')
- When building asset URLs in code, always run the path through encodeURI()/encodeURIComponent instead of string concatenation
- If a source file genuinely contains '%' in its name, rename it during the plugin build so emitted asset URLs stay clean
- Confirm you actually want the dev proxy — without a devUiUrl in plugin config this branch never runs
Example fix
// before
const url = `/_plugins/${pluginId}/ui/${rawFilePath}`; // rawFilePath may contain '%'
// after
const url = `/_plugins/${pluginId}/ui/${encodeURI(rawFilePath)}`; Defensive patterns
Strategy: validation
Validate before calling
const isDecodablePath = (p: string): boolean => {
try {
decodeURIComponent(p);
return true;
} catch {
return false;
}
};
const safePath = isDecodablePath(filePath) ? encodeURI(filePath) : null; Type guard
const isSafeAssetPath = (p: string): boolean =>
isDecodablePath(p) && !p.includes("://") && !p.startsWith("//") && !p.startsWith("\\\\"); Prevention
- Always construct asset URLs with encodeURI/encodeURIComponent, never raw concatenation of filenames that may contain '%'
- Keep '%' characters out of built asset filenames (rename at build time)
When it happens
Trigger: GET /_plugins/<id>/ui/badge%.png, /_plugins/<id>/ui/file%E0%A4%x.js, or any path containing an invalid percent-encoding, while the plugin has devUiUrl set for hot-reload development. Only reachable in the dev-proxy code path; in production or without devUiUrl the malformed path is handled by the static-file branch instead.
Common situations: Hand-typed or truncated asset URLs; filenames containing literal '%' that were not encodeURI'd when constructing the request; double-encoding bugs where a client encodes an already-encoded path (e.g. '%25' then mangled again); security probes with garbage encodings.
Related errors
- devUiUrl must use http or https protocol
- devUiUrl must target localhost
- Invalid status '${String(rawStatus)}'. Must be one of: ${PLU
- Request body is required
- "tool" is required and must be a string
AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18).
Data as JSON: /api/errors/08a41459dba32e91.
Report an issue: GitHub.