Description
The try/catch in Filesystem.resolve() (introduced in #16651) catches all exceptions from realpathSync, not just ENOENT. Errors like EACCES (permission denied), ELOOP (too many symlink levels), and ENOTDIR (component is not a directory) are silently swallowed, and the function falls back to the unresolved path.
This means:
- An
EACCES on a symlink target silently resolves to the symlink path instead of surfacing the permission error
- An
ELOOP (symlink cycle) silently resolves to the unresolvable symlink path instead of alerting the user
- Both could lead to
Instance.provide() creating a context keyed on a non-canonical path, re-introducing the duplicate-instance class of bugs for those edge cases
The codebase already has an isEnoent() type guard at filesystem.ts:50 used elsewhere. The fix is to narrow the catch:
export function resolve(p: string): string {
const resolved = pathResolve(windowsPath(p))
try {
return normalizePath(realpathSync(resolved))
} catch (e) {
if (isEnoent(e)) return normalizePath(resolved)
throw e
}
}
Steps to reproduce
- Create a symlink cycle:
ln -s a b && ln -s b a
- Or create a symlink to a permission-denied directory
- Run
opencode from a path involving that symlink
- Observe that the error is silently swallowed instead of surfaced
Operating System
All platforms
Description
The
try/catchinFilesystem.resolve()(introduced in #16651) catches all exceptions fromrealpathSync, not justENOENT. Errors likeEACCES(permission denied),ELOOP(too many symlink levels), andENOTDIR(component is not a directory) are silently swallowed, and the function falls back to the unresolved path.This means:
EACCESon a symlink target silently resolves to the symlink path instead of surfacing the permission errorELOOP(symlink cycle) silently resolves to the unresolvable symlink path instead of alerting the userInstance.provide()creating a context keyed on a non-canonical path, re-introducing the duplicate-instance class of bugs for those edge casesThe codebase already has an
isEnoent()type guard atfilesystem.ts:50used elsewhere. The fix is to narrow the catch:Steps to reproduce
ln -s a b && ln -s b aopencodefrom a path involving that symlinkOperating System
All platforms