different-ai/openwork · error · ApiError

file_too_large

file_too_large

Error message

Direct uploads support files up to ${DIRECT_UPLOAD_MAX_BYTES} bytes.

What it means

After resolving the file inside an authorized root, resolveAuthorizedFile stats it and enforces the DIRECT_UPLOAD_MAX_BYTES limit; files over the limit (and 0-byte files) get a 413 file_too_large ApiError.

Source

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

  for (const root of roots) {
    try {
      pushUniqueResolvedPath(realRoots, await realpath(root));
    } catch (error) {
      if (!isRecord(error) || error.code !== "ENOENT") throw error;
    }
  }
  const candidates = isAbsolute(requested)
    ? [resolve(requested)]
    : searchRoots(config, context, roots).map((root) => resolve(root, requested));
  for (const candidate of candidates) {
    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";

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Upload the file through the chunked/regular upload path instead of direct upload
  2. Compress or split the file so it fits under DIRECT_UPLOAD_MAX_BYTES
  3. Check the file isn't unexpectedly empty (size < 1 also triggers this) before uploading
  4. Host the file externally and reference by URL if it's genuinely too large

Example fix

// before
await uploadDirect(largeVideoPath); // 500MB
// after
await uploadChunked(largeVideoPath); // chunked path has its own limits
Defensive patterns

Strategy: validation

Validate before calling

if (file.size > DIRECT_UPLOAD_MAX_BYTES) return useChunkedUpload(file);

Try / catch

try { await uploadDirect(p); } catch (e) { if (e.code === "file_too_large") { await uploadChunked(p); } else throw e; }

Prevention

When it happens

Trigger: Direct-uploading a file whose stat size exceeds DIRECT_UPLOAD_MAX_BYTES, or an empty (0-byte) file, which is also rejected by the size < 1 check.

Common situations: Uploading large PDFs/videos/datasets via the direct upload path; symlinks resolving to big files; empty placeholder files created by scripts.

Related errors


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