{"record":{"id":"ce86ef3cec9d3d11","repo":"paperclipai/paperclip","slug":"filesystemextrapaths-index-must-use-access-ro","errorCode":null,"errorMessage":"filesystemExtraPaths[${index}] must use access \"ro\" or \"rw\" and an absolute path.","messagePattern":"filesystemExtraPaths\\[(.+?)\\] must use access \"ro\" or \"rw\" and an absolute path\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/local-process-sandbox.ts","lineNumber":505,"sourceCode":"  }\n\n  args.push(\"--chdir\", cwd, \"--\", executable, ...executableArgs);\n  return { command: bwrapCommand, args, cwd: \"/\", env, cleanup };\n}\n\nexport function parseLocalProcessSandboxExtraPaths(value: unknown): LocalProcessSandboxPath[] {\n  if (!Array.isArray(value)) return [];\n  return value.map((entry, index) => {\n    if (typeof entry === \"string\") {\n      return { path: normalizeAbsolutePath(entry, `filesystemExtraPaths[${index}]`), access: \"ro\" };\n    }\n    if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n      throw new Error(`filesystemExtraPaths[${index}] must be an absolute path or { path, access } object.`);\n    }\n    const raw = entry as Record<string, unknown>;\n    const access = raw.access === \"rw\" ? \"rw\" : raw.access === \"ro\" || raw.access == null ? \"ro\" : null;\n    if (!access || typeof raw.path !== \"string\") {\n      throw new Error(`filesystemExtraPaths[${index}] must use access \"ro\" or \"rw\" and an absolute path.`);\n    }\n    return { path: normalizeAbsolutePath(raw.path, `filesystemExtraPaths[${index}].path`), access };\n  });\n}\n","sourceCodeStart":487,"sourceCodeEnd":510,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/local-process-sandbox.ts#L487-L510","documentation":"Thrown by parseLocalProcessSandboxExtraPaths when an entry passed the shape check (it is a plain object) but its access field is something other than \"ro\", \"rw\", or null/undefined, OR its path field is missing or not a string. The access normalization at local-process-sandbox.ts:503 produces null for any other literal; combined with a missing/non-string path, the entry is rejected.","triggerScenarios":"filesystemExtraPaths: [{ path: \"/etc\", access: \"readonly\" }] (wrong literal), [{ access: \"ro\" }] (path missing), [{ path: 123, access: \"ro\" }] (path not a string), or [{ path: \"/etc\", access: \"write\" }]. Each of these reaches the throw at local-process-sandbox.ts:504-506.","commonSituations":"Migration from another tool that uses \"readonly\"/\"write\", \"r\"/\"w\", or \"read\"/\"write\"; config typos; UI dropdowns that submit a label instead of the canonical value; or payload schemas that allow extra keys without validating the access vocabulary.","solutions":["Use exactly \"ro\" or \"rw\" for access (or omit it — undefined defaults to \"ro\").","Ensure path is present and a string.","Map legacy vocabulary at the config boundary: \"readonly\" -> \"ro\", \"write\" | \"w\" -> \"rw\".","Reject any object whose access is not in [undefined, null, \"ro\", \"rw\"] before parsing so the failure surfaces earlier."],"exampleFix":"// before\nparseLocalProcessSandboxExtraPaths([\n  { path: \"/etc\", access: \"readonly\" },\n  { access: \"ro\" },\n  { path: 123, access: \"rw\" },\n]);\n\n// after\nparseLocalProcessSandboxExtraPaths([\n  { path: \"/etc\", access: \"ro\" },\n  { path: \"/var/data\", access: \"ro\" },\n  { path: \"/var/log\", access: \"rw\" },\n]);","handlingStrategy":"validation","validationCode":"const ACCESS_ALIASES = { readonly: \"ro\", read: \"ro\", write: \"rw\", readwrite: \"rw\", w: \"rw\", r: \"ro\" } as Record<string, \"ro\" | \"rw\">;\nfunction normalizeExtraPath(entry: { path: unknown; access?: unknown }): { path: string; access: \"ro\" | \"rw\" } {\n  if (typeof entry.path !== \"string\" || entry.path.length === 0) throw new Error(\"extraPath.path must be a non-empty string\");\n  const rawAccess = typeof entry.access === \"string\" ? entry.access.toLowerCase() : undefined;\n  const access: \"ro\" | \"rw\" | undefined = rawAccess === undefined ? \"ro\" : ACCESS_ALIASES[rawAccess] ?? (rawAccess === \"ro\" || rawAccess === \"rw\" ? rawAccess : undefined);\n  if (!access) throw new Error(`Unsupported access: ${String(entry.access)}`);\n  return { path: entry.path, access };\n}","typeGuard":"function isExtraPathObject(value: unknown): value is { path: string; access?: 'ro' | 'rw' } {\n  if (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n  const raw = value as Record<string, unknown>;\n  return typeof raw.path === \"string\" && (raw.access === undefined || raw.access === \"ro\" || raw.access === \"rw\");\n}","tryCatchPattern":"try {\n  const parsed = parseLocalProcessSandboxExtraPaths(config.filesystemExtraPaths);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('must use access')) {\n    throw new ConfigError(`filesystemExtraPaths access must be \\\"ro\\\" or \\\"rw\\\" (typo? legacy vocabulary?)`, { cause: error });\n  }\n  throw error;\n}","preventionTips":["Use exactly \"ro\" or \"rw\" (omit access entirely for \"ro\").","Translate legacy vocabulary at the config boundary.","Lock the access enum in your schema validator."],"tags":["filesystem","config-validation","sandbox","typescript"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}