different-ai/openwork · error · ApiError

file_not_found

file_not_found

Error message

File was not found inside an authorized workspace root.

What it means

If none of the authorized roots resolves to a real file containing the requested path, resolveAuthorizedFile throws a 404 file_not_found ApiError, preventing path-traversal uploads outside the workspace.

Source

Thrown at apps/server/src/extensions/cloud-uploads.ts:145

    if (!roots.some((root) => isWithinRoot(candidate, root))) continue;
    try {
      const realCandidate = await realpath(candidate);
      if (!realRoots.some((root) => isWithinRoot(realCandidate, root))) continue;
      const info = await stat(realCandidate);
      if (!info.isFile()) continue;
      if (info.size < 1 || info.size > DIRECT_UPLOAD_MAX_BYTES) {
        throw new ApiError(413, "file_too_large", `Direct uploads support files up to ${DIRECT_UPLOAD_MAX_BYTES} bytes.`, {
          size: info.size,
          maxBytes: DIRECT_UPLOAD_MAX_BYTES,
        });
      }
      return realCandidate;
    } catch (error) {
      if (isRecord(error) && error.code === "ENOENT") continue;
      throw error;
    }
  }
  throw new ApiError(404, "file_not_found", "File was not found inside an authorized workspace root.", { path: requested });
}

function mimeTypeForPath(path: string) {
  const lower = path.toLowerCase();
  if (lower.endsWith(".pdf")) return "application/pdf";
  if (lower.endsWith(".docx")) return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
  if (lower.endsWith(".xlsx")) return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  if (lower.endsWith(".pptx")) return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
  if (lower.endsWith(".csv")) return "text/csv";
  if (lower.endsWith(".txt")) return "text/plain";
  if (lower.endsWith(".json")) return "application/json";
  if (lower.endsWith(".png")) return "image/png";
  if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
  return "application/octet-stream";
}

async function cloudUploadEndpoint(config: ServerConfig, suffix: string, dependencies: CloudUploadDependencies) {
  const cloud = await (dependencies.readCloudMcp ?? readConnectCloudMcp)(config);

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Verify the path exists on disk and is spelled correctly, and that it's inside a configured workspace root
  2. Pass an absolute path within an authorized root rather than a relative one
  3. Replace symlinks with the real file inside the root (symlinks escaping the root are rejected by the realpath check)
  4. Recreate the file if it was moved or deleted since selection

Example fix

// before
await upload("../secrets/creds.txt"); // outside workspace
// after
await upload("/Users/me/projects/app/notes/notes.pdf"); // inside root
Defensive patterns

Strategy: validation

Validate before calling

const real = await realpath(p); const inside = (await Promise.all(roots.map(r => realpath(r)))).some(r => real.startsWith(r + sep)); if (!inside) throw new Error("outside workspace");

Try / catch

try { await uploadCloud(p); } catch (e) { if (e.code === "file_not_found") { promptRepickFile(); } else throw e; }

Prevention

When it happens

Trigger: Requesting an upload of a path that doesn't exist, or exists but lies outside every authorized root (including symlinked paths whose realpath escapes the roots).

Common situations: Typo in the path; file deleted before upload; absolute path outside the workspace; relative path resolving against the wrong cwd; symlink pointing outside the root (realpath check rejects it).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/1d4a579459e13047. Report an issue: GitHub.