Skip to content

fix(path): do not decode percent-encoding in raw filesystem paths#18356

Closed
ernestodeoliveira wants to merge 1 commit intoanomalyco:devfrom
ernestodeoliveira:fix/preserve-literal-percent-in-raw-paths
Closed

fix(path): do not decode percent-encoding in raw filesystem paths#18356
ernestodeoliveira wants to merge 1 commit intoanomalyco:devfrom
ernestodeoliveira:fix/preserve-literal-percent-in-raw-paths

Conversation

@ernestodeoliveira
Copy link

Summary

Fixes #18285.

When a project path contains a literal percent-encoded sequence in the directory name (e.g. D:\First%20Second), opencode was silently decoding %20 to a space, resolving the path as D:\First Second — a different, non-existent directory.

Root Cause

In packages/app/src/context/file/path.ts, the normalize() function always called decodeFilePath() (which runs decodeURIComponent()) on the raw input:

// Before: unconditional decode — breaks literal % in folder names
let path = unquoteGitPath(decodeFilePath(stripQueryAndHash(stripFileProtocol(input))))

decodeFilePath is only needed when the input is a file:// URL (to convert file:///home/user/My%20Docs/home/user/My Docs). Raw filesystem paths — Windows or Unix — may contain literal %20 or other %XX sequences as part of the actual folder/file name. Decoding those is incorrect.

Fix

Guard decodeFilePath behind an isUrl check:

// Only decode percent-encoding for file:// URLs — raw filesystem paths may
// contain literal % characters (e.g. D:\First%20Second) that must be preserved.
const isUrl = input.startsWith("file://") || input.startsWith("file:\\")
let path = unquoteGitPath(
  isUrl
    ? decodeFilePath(stripQueryAndHash(stripFileProtocol(input)))
    : stripQueryAndHash(stripFileProtocol(input)),
)

Testing

Added 2 regression tests to path.test.ts:

  1. Literal %20 in raw Windows path is preservedD:\First%20Second\file.txt normalizes correctly without decoding
  2. %20 in file:// URL is still decodedfile:///home/user/My%20Documents/file.txt continues to work as before

All 39 existing tests pass.

Notes

The typecheck pre-push hook fails on @opencode-ai/enterprise (src/custom-elements.d.ts) — this error exists on dev before this change and is unrelated to this fix.

When a project path contains a literal percent-encoded sequence in the
directory name (e.g. D:\First%20Second), normalize() was calling
decodeURIComponent() unconditionally, silently converting the literal
%20 to a space and resolving a different — non-existent — directory.

Fix: apply decodeFilePath only when the input is a file:// URL.
Raw filesystem paths (Windows or Unix) may contain literal % characters
that are part of the folder/file name and must not be decoded.

Adds regression tests for both the preserved-literal case and the
expected decode-on-file-url case.

Fixes anomalyco#18285
@github-actions github-actions bot added the needs:compliance This means the issue will auto-close after 2 hours. label Mar 20, 2026
@github-actions
Copy link
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions
Copy link
Contributor

This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.

Feel free to open a new pull request that follows our guidelines.

@github-actions github-actions bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Mar 20, 2026
@github-actions github-actions bot closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cannot handle escape sequence in path

1 participant