different-ai/openwork · error · ApiError
invalid_path
invalid_path
Error message
Only supported text artifact files can be read inline
What it means
The GET /workspace/:id/files/content endpoint reads a workspace file inline, but only for paths passing isSupportedWorkspaceTextFilePath() — i.e. recognized text artifact file types. Any other path (binary files, unknown extensions, dotfiles, no extension) is rejected with a 400 invalid_path ApiError before existence checks. This prevents dumping binaries or arbitrary files through the inline content API.
Source
Thrown at apps/server/src/routes/files.ts:842
}
items.push({ ok: false, type, code: "invalid_operation", message: `Unsupported operation type: ${type}` });
} catch (error) {
const message = error instanceof Error ? error.message : "Operation failed";
items.push({ ok: false, type, code: "operation_failed", message });
}
}
const events = fileSessions.listWorkspaceEvents(workspace.id, Number.MAX_SAFE_INTEGER);
return jsonResponse({ items, cursor: events.cursor });
});
addRoute(routes, "GET", "/workspace/:id/files/content", "client", async (ctx) => {
const workspace = await resolveWorkspace(config, ctx.params.id);
const requested = ctx.url.searchParams.get("path") ?? "";
const relativePath = normalizeWorkspaceRelativePath(requested, { allowSubdirs: true });
if (!isSupportedWorkspaceTextFilePath(relativePath)) {
throw new ApiError(400, "invalid_path", "Only supported text artifact files can be read inline");
}
const absPath = resolveSafeChildPath(workspace.path, relativePath);
if (!(await exists(absPath))) {
throw new ApiError(404, "file_not_found", "File not found");
}
const info = await stat(absPath);
if (!info.isFile()) {
throw new ApiError(404, "file_not_found", "File not found");
}
const maxBytes = FILE_SESSION_MAX_FILE_BYTES;
if (info.size > maxBytes) {
throw new ApiError(413, "file_too_large", "File exceeds size limit", { maxBytes, size: info.size });
}
const content = await readFile(absPath, "utf8");
return jsonResponse({ path: relativePath, content, bytes: info.size, updatedAt: info.mtimeMs });View on GitHub (pinned to 2b7df46e8a)
Solutions
- Request only supported text artifact paths (e.g. *.md, *.ts, *.json — check isSupportedWorkspaceTextFilePath for the exact allowlist)
- Use the appropriate download/asset endpoint for binary files instead of the inline content API
- Fix the requested path's extension/case if it is actually a supported type
- List workspace files first and filter to supported text paths
Example fix
// before
const res = await api.get(`/workspace/${id}/files/content?path=logo.png`); // 400
// after
if (/\.(md|txt|ts|tsx|js|json|ya?ml|toml)$/.test(path)) {
const res = await api.get(`/workspace/${id}/files/content?path=${encodeURIComponent(path)}`);
} else {
const res = await api.get(`/workspace/${id}/files/download?path=${encodeURIComponent(path)}`);
} Defensive patterns
Strategy: type-guard
Validate before calling
const SUPPORTED_TEXT_RE = /\.(md|txt|ts|tsx|js|jsx|json|ya?ml|toml|css|html|sh)$/i;
if (!SUPPORTED_TEXT_RE.test(relativePath)) {
throw new Error(`use download endpoint for non-text file: ${relativePath}`);
} Type guard
function isSupportedTextPath(p: string): boolean {
return /\.(md|txt|ts|tsx|js|jsx|json|ya?ml|toml|css|html|sh)$/i.test(p);
} Try / catch
try {
const res = await api.get(`/workspace/${id}/files/content?path=${encodeURIComponent(p)}`);
} catch (e) {
if (e.code === "invalid_path") {
return api.get(`/workspace/${id}/files/download?path=${encodeURIComponent(p)}`); // binary route
}
throw e;
} Prevention
- Route binary/asset files to the download endpoint, not the inline content API
- Maintain a shared allowlist of supported text extensions matching the server's
- Check the file extension before requesting inline content
- Beware dotfiles and extensionless paths — they fail the supported-type check
When it happens
Trigger: Requesting content for a binary file (.png, .zip, .db), an unsupported extension (.exe, .pdf), a path with no extension, or a dotfile like .env; also a misspelled/mis-cased extension that fails the supported-type check.
Common situations: A UI tries to preview an uploaded image via the text-content endpoint; a script assumes any path under the workspace is readable; a symlink or generated file has an unexpected extension.
Related errors
- Select a local workspace before starting the local server/en
- app.error_connect_first
- invalid_command_template
- invalid_env_key
- invalid_payload
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/702b01f463f3e484.
Report an issue: GitHub.