{"record":{"id":"5aee5d1c5bef4a9e","repo":"vercel/ai","slug":"sandbox-workspace-mirror-received-an-invalid-relat","errorCode":null,"errorMessage":"Sandbox workspace mirror received an invalid relative path: ${inputPath}","messagePattern":"Sandbox workspace mirror received an invalid relative path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/harness-pi/src/pi-workspace-mirror.ts","lineNumber":45,"sourceCode":" * copied as real files. `.agents/skills` is frequently a symlink to a `skills`\n * directory living elsewhere in the workspace; a mirrored symlink would dangle\n * because its target falls outside the scoped mirror, so the linked content is\n * walked and copied verbatim instead.\n */\nconst PI_CONFIG_DIRS = ['.pi', '.agents'] as const;\nconst PI_CONTEXT_FILENAMES = ['AGENTS.md', 'AGENTS.MD'] as const;\n\nfunction normalizeRelativePath(inputPath: string): string {\n  const normalized = inputPath.split(path.posix.sep).join(path.sep);\n  const relative = path.normalize(normalized);\n  if (\n    relative === '' ||\n    relative === '.' ||\n    path.isAbsolute(relative) ||\n    relative === '..' ||\n    relative.startsWith(`..${path.sep}`)\n  ) {\n    throw new Error(\n      `Sandbox workspace mirror received an invalid relative path: ${inputPath}`,\n    );\n  }\n  return relative;\n}\n\nasync function readCommandOutput(\n  sandbox: Experimental_SandboxSession,\n  command: string,\n): Promise<string> {\n  const result = await sandbox.run({ command });\n  if (result.exitCode != null && result.exitCode !== 0) {\n    throw new Error(\n      result.stderr ||\n        result.stdout ||\n        `Sandbox command failed with exit code ${result.exitCode}`,\n    );\n  }","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/harness-pi/src/pi-workspace-mirror.ts#L27-L63","documentation":"normalizeRelativePath in the sandbox workspace mirror validates that every path it mirrors is a safe relative path inside the workspace. It rejects empty paths, '.', '..', absolute paths, and any path escaping the root via a leading '..' segment. This prevents the mirror from reading or writing files outside the sandboxed workspace directory.","triggerScenarios":"Calling normalizeRelativePath (via relativePath/buildRequiredDirectories/current/normalizedPath call paths) with an absolute path like '/etc/passwd', a traversal path like '../secrets.txt', or an empty/'.' value as inputPath.","commonSituations":"Passing OS-absolute paths from file pickers or CLI args instead of workspace-relative ones; joining paths with '..' to 'reuse' a base directory; Windows-style backslash paths that resolve as absolute on the host; misconfigured workspace root causing computed relative paths to start with '..'.","solutions":["Convert the input to a workspace-relative path before handing it to the mirror (e.g. path.relative(workspaceRoot, absPath)).","Verify the workspace root is correct so relative resolution does not yield '..' segments.","Normalize and strip leading separators; reject or remap absolute inputs at your API boundary.","Catch the error and skip/skip-log the offending path rather than failing the whole mirror operation."],"exampleFix":"// before\nmirror.current({ path: '/home/me/project/src/index.ts' });\n// after\nconst rel = path.relative(workspaceRoot, '/home/me/project/src/index.ts');\nmirror.current({ path: rel }); // 'src/index.ts'","handlingStrategy":"validation","validationCode":"const path = require('node:path');\nfunction toWorkspaceRelative(inputPath, workspaceRoot) {\n  const rel = path.isAbsolute(inputPath)\n    ? path.relative(workspaceRoot, inputPath)\n    : inputPath;\n  if (rel === '' || rel === '.' || path.isAbsolute(rel) || rel === '..' || rel.startsWith(`..${path.sep}`)) {\n    throw new Error(`Path escapes workspace: ${inputPath}`);\n  }\n  return rel;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await mirror.current({ path: rel });\n} catch (err) {\n  if (String(err.message).includes('invalid relative path')) {\n    logger.warn('Skipping path outside workspace:', err.message);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Always pass workspace-relative paths, never absolute OS paths","Compute relativity against the same workspaceRoot the mirror uses","Refuse '..' segments at your API boundary","On Windows, normalize separators before validation"],"tags":["path-validation","security","sandbox","workspace"],"backgroundTag":"invalid-relative-path","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}