stablyai/orca · error · Error

Unsafe computer screenshot temp path: ${outputDir}

Error message

Unsafe computer screenshot temp path: ${outputDir}

What it means

Thrown by computerScreenshotTempDir() after creating the screenshot temp dir: if the resolved path is not a directory or is a symbolic link, the path is considered unsafe for writing screenshots. This guards against symlink-traversal and non-directory inode attacks on a path the agent writes screenshot data into, since the directory is created with mode 0700 but an attacker could have pre-placed a symlink or file.

Source

Thrown at src/cli/computer-format.ts:110

    } as RuntimeRpcSuccess<TResult>
  } catch {
    // Why: temp-file export is an ergonomics optimization; keep inline screenshot
    // data when disk, permissions, or path validation would otherwise fail --json.
    return response
  }
}

const COMPUTER_SCREENSHOT_TTL_MS = 24 * 60 * 60 * 1000
const COMPUTER_SCREENSHOT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000
const COMPUTER_SCREENSHOT_CLEANUP_MARKER = '.last-cleanup'

function computerScreenshotTempDir(): string {
  const outputDir =
    process.env.ORCA_COMPUTER_SCREENSHOT_TMPDIR || join(tmpdir(), 'orca-computer-use')
  mkdirSync(outputDir, { recursive: true, mode: 0o700 })
  const stat = lstatSync(outputDir)
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
    throw new Error(`Unsafe computer screenshot temp path: ${outputDir}`)
  }
  if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) {
    throw new Error(`Computer screenshot temp path is not owned by the current user: ${outputDir}`)
  }
  chmodSync(outputDir, 0o700)
  return outputDir
}

function cleanupComputerScreenshots(outputDir: string): void {
  const now = Date.now()
  const markerPath = join(outputDir, COMPUTER_SCREENSHOT_CLEANUP_MARKER)
  try {
    // Why: agents can call computer-use CLI commands in loops; a marker keeps
    // temp cleanup from becoming a synchronous directory scan per screenshot.
    if (statSync(markerPath).mtimeMs > now - COMPUTER_SCREENSHOT_CLEANUP_INTERVAL_MS) {
      return
    }
  } catch {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Point ORCA_COMPUTER_SCREENSHOT_TMPDIR at a real directory you own, or unset it to use the default.
  2. Remove the offending non-directory/symlink entry at the resolved path so mkdirSync can recreate it as a directory.
  3. Avoid symlinks in the screenshot temp path; resolve them before setting the env var.

Example fix

# before
export ORCA_COMPUTER_SCREENSHOT_TMPDIR=/tmp/my-link   # symlink

# after
export ORCA_COMPUTER_SCREENSHOT_TMPDIR=/tmp/real-dir   # mkdir -p first
Defensive patterns

Strategy: validation

Validate before calling

import { lstatSync } from 'node:fs'

function safeScreenshotTmpdir(p: string): string {
  const st = lstatSync(p)
  if (!st.isDirectory() || st.isSymbolicLink()) {
    throw new Error(`Refusing unsafe screenshot tmpdir: ${p}`)
  }
  return p
}

Type guard

function isRealDirectory(p: string): boolean {
  try {
    const st = lstatSync(p)
    return st.isDirectory() && !st.isSymbolicLink()
  } catch {
    return false
  }
}

Prevention

When it happens

Trigger: ORCA_COMPUTER_SCREENSHOT_TMPDIR (or the default <tmpdir>/orca-computer-use) resolves to a regular file, a device, or a symlink rather than a real directory owned by the process.

Common situations: Setting ORCA_COMPUTER_SCREENSHOT_TMPDIR to an existing file or a symlink, a shared /tmp where another user or a broken cleanup script left a file at that name, or pointing the env var at a path inside a symlinked mount.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/77ce9dd5f38946f0. Report an issue: GitHub.