{"record":{"id":"9701844604664e2c","repo":"different-ai/openwork","slug":"invalid-path","errorCode":"invalid_path","errorMessage":"Path must be absolute","messagePattern":"Path must be absolute","errorType":"error_code","errorClass":"ApiError","httpStatus":400,"severity":"error","filePath":"apps/server/src/paths.ts","lineNumber":7,"sourceCode":"import { realpath } from \"node:fs/promises\";\nimport { isAbsolute, resolve, sep } from \"node:path\";\nimport { ApiError } from \"./errors.js\";\n\nexport function assertAbsolute(path: string): void {\n  if (!isAbsolute(path)) {\n    throw new ApiError(400, \"invalid_path\", \"Path must be absolute\");\n  }\n}\n\nexport async function resolveWithinRoot(root: string, ...segments: string[]): Promise<string> {\n  const resolvedRoot = await realpath(root);\n  const candidate = resolve(resolvedRoot, ...segments);\n  const resolvedCandidate = await realpath(candidate).catch(() => candidate);\n  if (resolvedCandidate === resolvedRoot) return candidate;\n  if (!resolvedCandidate.startsWith(resolvedRoot + sep)) {\n    throw new ApiError(400, \"path_escape\", \"Path escapes workspace root\");\n  }\n  return candidate;\n}\n","sourceCodeStart":1,"sourceCodeEnd":21,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/paths.ts#L1-L21","documentation":"assertAbsolute validates that a supplied filesystem path is absolute before the server resolves it; relative paths (or empty strings) raise this 400 ApiError with code invalid_path. The server API never interprets paths relative to its own CWD.","triggerScenarios":"Calling filePath or any path-taking API with a relative path like \"src/foo.ts\" or \"./notes.txt\", an empty path, or a path built from a URL-encoded relative segment.","commonSituations":"Clients assuming server-side CWD resolution; passing workspace-relative paths from UI code; forgetting path.resolve on the client; tests reusing relative fixtures.","solutions":["Convert the path to absolute on the caller side (path.resolve / path.join with the workspace root) before sending.","Prefix with the workspace root directory you registered with the server.","Trim/handle empty strings before calling.","In tests, build absolute paths from a tmpdir instead of fixtures."],"exampleFix":"// before\nawait api.filePath({ path: \"src/index.ts\" });\n// after\nimport { resolve } from \"node:path\";\nawait api.filePath({ path: resolve(workspaceRoot, \"src/index.ts\") });","handlingStrategy":"validation","validationCode":"import { isAbsolute, resolve } from \"node:path\";\nfunction toAbsolute(p: string, workspaceRoot: string): string {\n  const abs = resolve(workspaceRoot, p);\n  if (!isAbsolute(abs)) throw new Error(`Path must be absolute: ${p}`);\n  return abs;\n}","typeGuard":"function isAbsolutePath(p: string): boolean {\n  return typeof p === \"string\" && p.length > 0 && isAbsolute(p);\n}","tryCatchPattern":"import { ApiError } from \"./errors.js\";\ntry {\n  assertAbsolute(userPath);\n} catch (err) {\n  if (err instanceof ApiError && err.code === \"invalid_path\") {\n    throw new ApiError(400, \"invalid_path\", `Provide an absolute path, got: ${userPath}`);\n  }\n  throw err;\n}","preventionTips":["Always build paths with path.resolve(root, relative) before calling server APIs.","Never rely on server-side CWD; the server has no concept of client CWD.","Reject empty/whitespace paths in UI forms before submission.","In tests, generate absolute paths from tmpdir fixtures."],"tags":["path","validation","api"],"backgroundTag":"relative-path-not-allowed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}