CherryHQ/cherry-studio · error · Error

Session workspace is unavailable

Error message

Session workspace is unavailable: ${workspaceRoot}

What it means

Thrown by resolveWorkspaceFile() when realpath(workspaceRoot) fails with ENOENT or ENOTDIR. Unlike the per-file 'File not found' error, this specifically indicates the workspace root directory itself is gone. The comment in the source explains: a bare ENOENT naming the root would read like 'your file_path is wrong', so this is wrapped to tell the agent the session workspace itself is unavailable, preventing wasted retries on alternative file paths.

Solutions

  1. Verify the session workspace directory still exists at the path given by workspaceRoot — if it was cleaned, recreate it or start a new session.
  2. If this is a crash-restart path, ensure the session lifecycle recreates the workspace directory before rehydrating agent state.
  3. If running on a temp directory, increase the OS temp-cleanup interval or move workspaces to a stable application-managed path via application.getPath().
Defensive patterns

Strategy: try-catch

Validate before calling

import { exists } from '@main/utils/file'

// Check workspace exists before resolving files
const workspaceExists = await exists(workspaceRoot)
if (!workspaceExists) {
  throw new Error(`Workspace not found, recreating or starting new session: ${workspaceRoot}`)
}

Try / catch

try {
  return await resolveWorkspaceFile(workspaceRoot, userPath)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Session workspace is unavailable:')) {
    // The workspace itself is gone — no retry on alternative paths will help
    logger.error('Session workspace missing', { workspaceRoot })
    throw new Error('Session workspace was deleted. Start a new session.')
  }
  throw error
}

Prevention

When it happens

Trigger: Called from agent document tools (cherryDocumentTools.ts:84, cherryAutonomyTools.ts:436) when an agent attempts to read a workspace file but the workspace directory was deleted, unmounted, or the session's workspacePath was never created. This is a caller-invariant violation: the workspace root is expected to exist for the entire session lifetime.

Common situations: The session workspace temp directory was cleaned by an OS temp-reaper or manual cleanup while the agent was still running; a crash-restart scenario where the workspace path was persisted but the directory was not recreated; the workspace was on a removable drive that was ejected; a workspace creation race where the agent started before the directory was fully set up.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/b007c54d8d0054b9. Report an issue: GitHub.

Appendix: source

Thrown at src/main/ai/channels/security/WorkspaceFileGuard.ts:33

 * inside it. `realpath` defeats `../` and symlink escape; reading happens on a single
 * fd over the canonical path so the stat/size check and the read see the same inode.
 *
 * This is defense-in-depth against traversal mistakes and prompt injection picking a
 * wrong path — not a sandbox against an agent with code execution (which can already
 * read arbitrary files and exfiltrate them as message text). See #16566.
 */
export async function resolveWorkspaceFile(workspaceRoot: string, userPath: string): Promise<FileAttachment> {
  const requested = path.resolve(workspaceRoot, userPath)

  let realRoot: string
  try {
    realRoot = await realpath(workspaceRoot)
  } catch (error) {
    // The root is a caller invariant, but if the session workspace is gone a bare ENOENT
    // naming the root reads like "your file_path is wrong" — wrap it so the agent doesn't
    // waste retries on other paths.
    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
      throw new Error(`Session workspace is unavailable: ${workspaceRoot}`)
    }
    throw error
  }

  let realTarget: string
  try {
    realTarget = await realpath(requested)
  } catch (error) {
    if (isErrnoException(error) && (error.code === 'ENOENT' || error.code === 'ENOTDIR')) {
      throw new Error(`File not found in workspace: ${userPath}`)
    }
    throw error
  }

  if (realTarget !== realRoot && !realTarget.startsWith(realRoot + path.sep)) {
    throw new Error(`Path is outside the workspace: ${userPath}`)
  }

View on GitHub (pinned to 726446b54c)