{"record":{"id":"8d7c74bff5a96d44","repo":"CherryHQ/cherry-studio","slug":"file-not-found-in-workspace-userpath","errorCode":null,"errorMessage":"File not found in workspace: ${userPath}","messagePattern":"File not found in workspace: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ai/channels/security/WorkspaceFileGuard.ts","lineNumber":43,"sourceCode":"  let realRoot: string\n  try {\n    realRoot = await realpath(workspaceRoot)\n  } catch (error) {\n    // The root is a caller invariant, but if the session workspace is gone a bare ENOENT\n    // naming the root reads like \"your file_path is wrong\" — wrap it so the agent doesn't\n    // waste retries on other paths.\n    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {\n      throw new Error(`Session workspace is unavailable: ${workspaceRoot}`)\n    }\n    throw error\n  }\n\n  let realTarget: string\n  try {\n    realTarget = await realpath(requested)\n  } catch (error) {\n    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {\n      throw new Error(`File not found in workspace: ${userPath}`)\n    }\n    throw error\n  }\n\n  if (realTarget !== realRoot && !realTarget.startsWith(realRoot + path.sep)) {\n    throw new Error(`Path is outside the workspace: ${userPath}`)\n  }\n\n  return readCanonicalLocalFile(requested, realTarget, userPath)\n}\n","sourceCodeStart":25,"sourceCodeEnd":54,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/src/main/ai/channels/security/WorkspaceFileGuard.ts#L25-L54","documentation":"Thrown by resolveWorkspaceFile() when realpath(requested) — the resolved target path — fails with ENOENT or ENOTDIR. This means the specific file the agent or user requested does not exist (or a path component is not a directory) within the workspace. The realpath call also resolves symlinks, so a dangling symlink to a non-existent target triggers this as well.","triggerScenarios":"Called from agent document/autonomy tools when the agent supplies a userPath that doesn't resolve to an existing file. Triggers when: the file was never created, was deleted between listing and read, a path component is a file instead of a directory (ENOTDIR), or a symlink points to a target that no longer exists.","commonSituations":"An agent hallucinated a file path from the workspace listing; the file was deleted by another process or agent step between the directory listing and the read attempt; the agent used a path from a prior session that no longer exists; a build step cleaned output directories that the agent expected to read.","solutions":["Re-list the workspace directory to confirm the file still exists before retrying the read.","Check for typos or path-component case sensitivity (especially on case-sensitive filesystems).","If the file was created by a prior agent step, verify that step completed successfully before this read."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { exists } from '@main/utils/file'\n\nconst resolvedPath = path.resolve(workspaceRoot, userPath)\nif (!(await exists(resolvedPath))) {\n  // File doesn't exist — re-list the workspace to help the agent self-correct\n  logger.warn('Requested file not found in workspace', { userPath, resolvedPath })\n  return null\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await resolveWorkspaceFile(workspaceRoot, userPath)\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('File not found in workspace:')) {\n    // Return null or re-list the workspace directory so the agent can pick a valid file\n    return null\n  }\n  throw error\n}","preventionTips":["Re-list the workspace directory before reading files if there is any chance of concurrent modification.","Cache workspace file listings with a short TTL to catch stale paths.","When an agent selects a file path, validate it against the current listing before attempting a read."],"tags":["filesystem","workspace","file-not-found","enoent","channels"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}