{"record":{"id":"d1cef8b174e7e8e4","repo":"paperclipai/paperclip","slug":"workspace-durable-seed-invalid","errorCode":"workspace_durable_seed_invalid","errorMessage":"workspace_durable_seed_invalid","messagePattern":"workspace_durable_seed_invalid","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/sandbox-managed-runtime.ts","lineNumber":768,"sourceCode":"\nasync function sha256File(filePath: string): Promise<string> {\n  return await new Promise((resolveDigest, rejectDigest) => {\n    const digest = createHash(\"sha256\");\n    const stream = createReadStream(filePath);\n    stream.on(\"data\", (chunk) => digest.update(chunk));\n    stream.on(\"error\", rejectDigest);\n    stream.on(\"end\", () => resolveDigest(digest.digest(\"hex\")));\n  });\n}\n\nasync function copyDurableSeedArchive(input: {\n  sourcePath: string;\n  targetPath: string;\n  expectedSha256?: string | null;\n}): Promise<void> {\n  const source = await fs.lstat(input.sourcePath);\n  if (source.isSymbolicLink() || !source.isFile()) {\n    throw new Error(\"workspace_durable_seed_invalid\");\n  }\n  if (\n    input.expectedSha256 &&\n    (await sha256File(input.sourcePath)) !== input.expectedSha256\n  ) {\n    throw new Error(\"workspace_durable_seed_digest_mismatch\");\n  }\n  await fs.copyFile(input.sourcePath, input.targetPath);\n}\n\nasync function persistDurableSeedArchive(input: {\n  sourcePath: string;\n  targetPath: string;\n}): Promise<void> {\n  const parent = path.dirname(input.targetPath);\n  await fs.mkdir(parent, { recursive: true, mode: 0o700 });\n  const parentStat = await fs.lstat(parent);\n  if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) {","sourceCodeStart":750,"sourceCodeEnd":786,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/adapter-utils/src/sandbox-managed-runtime.ts#L750-L786","documentation":"copyDurableSeedArchive validates each durable seed source before copying it into a sandbox-managed runtime. Error 'workspace_durable_seed_invalid' is thrown when the source path (lstat) is neither a regular file nor acceptable — specifically when it is a symbolic link or not a plain file (e.g., a directory, socket, or missing via lstat quirk). Seeds must be real files so they can be hashed and archived safely.","triggerScenarios":"prepareSandboxManagedRuntime calls copyDurableSeedArchive with a sourcePath that lstat reports as a symlink, a directory, or any non-regular file. A companion error 'workspace_durable_seed_digest_mismatch' covers the sha256 mismatch case.","commonSituations":"Workspace seed config points at a symlinked file (common with symlinked dotfiles or package-manager links); the seed path is a directory; the file was replaced by a symlink between config and runtime prep; path typo resolves to the wrong inode type.","solutions":["Point the seed sourcePath at a real regular file: replace the symlink with `cp --remove-destination \"$(readlink -f path)\" path` or resolve it before configuring.","Verify with `ls -la <sourcePath>` / `stat -c '%F'` that the entry is 'regular file'.","If the seed should be a directory, use a directory seed mechanism or an archive file instead.","Re-run prepareSandboxManagedRuntime after fixing the path; the error is raised during runtime preparation, not mid-run."],"exampleFix":"// before: seed config pointing at a symlink\n{ sourcePath: \"/home/dev/.aws/config\", targetPath: \"/workspace/.aws/config\" }  // .aws/config -> dotfiles/aws-config (symlink)\n// after: materialize a real file first\nexecSync(`cp --remove-destination \"$(readlink -f /home/dev/.aws/config)\" /home/dev/.aws/config`);\n// then seed with the same sourcePath","handlingStrategy":"validation","validationCode":"const st = await fs.lstat(seed.sourcePath);\nif (st.isSymbolicLink() || !st.isFile()) {\n  throw new Error(`durable seed ${seed.sourcePath} must be a regular file (got ${st.isSymbolicLink() ? \"symlink\" : \"other\"})`);\n}","typeGuard":"function isRegularFileSync(p: string): boolean {\n  try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return false; }\n}","tryCatchPattern":"try {\n  await prepareSandboxManagedRuntime(opts);\n} catch (err) {\n  if (err.message === \"workspace_durable_seed_invalid\") {\n    console.error(`seed source ${opts.seedSourcePath} is a symlink or not a regular file; materialize it first`);\n  } else if (err.message === \"workspace_durable_seed_digest_mismatch\") {\n    console.error(\"seed file changed; refresh expectedSha256\");\n  }\n  throw err;\n}","preventionTips":["Never configure symlinked paths as durable seeds; resolve with readlink -f at config time.","Validate seed paths with stat (file type + sha256) before calling prepareSandboxManagedRuntime.","Recompute expectedSha256 whenever seed files are updated.","Document that seeds must be regular files so tooling and users avoid links/directories."],"tags":["sandbox","filesystem","validation","symlink"],"backgroundTag":"incompatible-source-type","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}