{"record":{"id":"3bb3a7702a9e7b0d","repo":"paperclipai/paperclip","slug":"filesystemextrapaths-index-must-be-an-absolute","errorCode":null,"errorMessage":"filesystemExtraPaths[${index}] must be an absolute path or { path, access } object.","messagePattern":"filesystemExtraPaths\\[(.+?)\\] must be an absolute path or (.+?) object\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/local-process-sandbox.ts","lineNumber":500,"sourceCode":"    const proxyUrl = `http://127.0.0.1:${SANDBOX_PROXY_PORT}`;\n    env.HTTP_PROXY = proxyUrl;\n    env.HTTPS_PROXY = proxyUrl;\n    env.http_proxy = proxyUrl;\n    env.https_proxy = proxyUrl;\n  }\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":482,"sourceCodeEnd":510,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/local-process-sandbox.ts#L482-L510","documentation":"Thrown by parseLocalProcessSandboxExtraPaths when an entry in the filesystemExtraPaths array is neither a string nor a plain object. Strings are accepted (treated as { path: entry, access: \"ro\" }); plain objects are accepted if they have the right shape; everything else (number, boolean, null, array) is rejected.","triggerScenarios":"filesystemExtraPaths: [\"/usr/lib\", 42, true, null, [\"/etc\"], new URL(\"file:///etc\")] — every non-string, non-plain-object entry hits the throw at local-process-sandbox.ts:499-501. Arrays are explicitly rejected via Array.isArray(entry).","commonSituations":"Config loaded from YAML where unquoted values become numbers or booleans; JSON payloads with mixed shapes; a default value that leaks null into the array; or callers that pass a single object instead of an array of objects.","solutions":["Provide each entry as either a string (\"/usr/lib\") or an object ({ path: \"/usr/lib\", access: \"ro\" }).","Filter the array before parsing to drop nulls: entries.filter((e) => e != null).","Quote YAML entries that look like numbers or booleans to force string parsing.","Validate the array shape with a schema (zod z.array(z.union([z.string(), z.object({ path: z.string(), access: z.enum([\"ro\",\"rw\"]).optional() })]))) at the config boundary."],"exampleFix":"// before\nparseLocalProcessSandboxExtraPaths([\"/usr/lib\", 42, { path: \"/etc\", access: \"ro\" }]);\n\n// after\nparseLocalProcessSandboxExtraPaths([\"/usr/lib\", \"/opt/data\", { path: \"/etc\", access: \"ro\" }]);","handlingStrategy":"type-guard","validationCode":"function isExtraPathEntry(value: unknown): value is string | { path: string; access?: string } {\n  if (typeof value === \"string\") return true;\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}\n\nif (!Array.isArray(config.filesystemExtraPaths) || !config.filesystemExtraPaths.every(isExtraPathEntry)) {\n  throw new Error(\"filesystemExtraPaths must be string[] or { path, access? }[]\");\n}\nconst parsed = parseLocalProcessSandboxExtraPaths(config.filesystemExtraPaths);","typeGuard":"function isLocalProcessSandboxExtraPath(value: unknown): value is string | { path: string; access?: 'ro' | 'rw' } {\n  if (typeof value === \"string\") return true;\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.startsWith(\"filesystemExtraPaths[\")) {\n    throw new ConfigError(`Invalid filesystemExtraPaths: ${error.message}`, { cause: error });\n  }\n  throw error;\n}","preventionTips":["Type the field as (string | { path: string; access?: 'ro' | 'rw' })[] in the config schema.","Filter nulls before parsing: entries.filter((e) => e != null).","Quote YAML entries that look numeric/boolean so they parse as strings."],"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"}