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

  1. Fix the requesting URL so it is a valid percent-encoded path (a literal '%' must be sent as '%25')
  2. When building asset URLs in code, always run the path through encodeURI()/encodeURIComponent instead of string concatenation
  3. If a source file genuinely contains '%' in its name, rename it during the plugin build so emitted asset URLs stay clean
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@120ae5428f (2026-08-18). Data as JSON: /api/errors/08a41459dba32e91. Report an issue: GitHub.