CherryHQ/cherry-studio · error · Error
File not found: ${userPath}
Error message
File not found: ${userPath} What it means
Thrown by resolveLocalFile() when realpath(requestedPath) fails with ENOENT or ENOTDIR. resolveLocalFile is the unconstrained file resolver: it resolves relative paths from basePath and follows symlinks via realpath, but performs NO containment check — an absolute userPath escapes basePath by design (see the NOTE in the source comment). This error means the file simply does not exist at the resolved path.
Source
Thrown at src/main/ai/channels/security/localFileResolver.ts:82
} finally {
// Swallow close errors so they can't mask an in-flight resolution error.
await snapshot.close().catch(() => {})
}
}
/**
* Resolve and read a local file. Relative paths are resolved from `basePath`.
* NOTE: no containment check — an absolute `userPath` escapes `basePath`.
*/
export async function resolveLocalFile(basePath: string, userPath: string): Promise<FileAttachment> {
const requestedPath = path.resolve(basePath, userPath)
let canonicalPath: string
try {
canonicalPath = await realpath(AbsoluteFilePathSchema.parse(requestedPath))
} catch (error) {
if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
throw new Error(`File not found: ${userPath}`)
}
throw error
}
return readCanonicalLocalFile(requestedPath, canonicalPath, userPath)
}
View on GitHub (pinned to 726446b54c)
Solutions
- Verify the file exists at the resolved absolute path before calling resolveLocalFile.
- If the path is workspace-relative, use resolveWorkspaceFile instead — it provides better error messages and containment.
- Check for typos, case sensitivity issues, or missing path components.
Defensive patterns
Strategy: validation
Validate before calling
import { exists } from '@main/utils/file'
const resolvedPath = path.resolve(basePath, userPath)
if (!(await exists(resolvedPath))) {
logger.warn('File does not exist', { userPath, resolvedPath })
return null
} Try / catch
try {
return await resolveLocalFile(basePath, userPath)
} catch (error) {
if (error instanceof Error && error.message.startsWith('File not found:')) {
return null
}
throw error
} Prevention
- Verify file existence before calling resolveLocalFile.
- Prefer resolveWorkspaceFile when containment is needed — it provides better error messages.
- Remember that resolveLocalFile does NOT check containment — an absolute userPath escapes basePath by design.
When it happens
Trigger: Called from cherryDocumentTools.ts:90 when the tool decides the path is not a workspace-relative path and falls back to the unconstrained resolver. Fails when the file does not exist, a path component is not a directory (ENOTDIR), or a symlink is dangling.
Common situations: An agent supplied an absolute path to a file that does not exist on the host; a relative path that, when resolved from basePath, points to a non-existent location; the file was deleted between a prior existence check and the realpath call.
Related errors
- File not found in workspace: ${userPath}
- File not found: ${displayPath}
- Session workspace is unavailable: ${workspaceRoot}
- Path is outside the workspace: ${userPath}
- Not a regular file: ${displayPath}
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/dc8d62dac817be88.
Report an issue: GitHub.