paperclipai/paperclip · error · Error

workspace_durable_seed_invalid

workspace_durable_seed_invalid

Error message

workspace_durable_seed_invalid

What it means

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.

Source

Thrown at packages/adapter-utils/src/sandbox-managed-runtime.ts:768

async function sha256File(filePath: string): Promise<string> {
  return await new Promise((resolveDigest, rejectDigest) => {
    const digest = createHash("sha256");
    const stream = createReadStream(filePath);
    stream.on("data", (chunk) => digest.update(chunk));
    stream.on("error", rejectDigest);
    stream.on("end", () => resolveDigest(digest.digest("hex")));
  });
}

async function copyDurableSeedArchive(input: {
  sourcePath: string;
  targetPath: string;
  expectedSha256?: string | null;
}): Promise<void> {
  const source = await fs.lstat(input.sourcePath);
  if (source.isSymbolicLink() || !source.isFile()) {
    throw new Error("workspace_durable_seed_invalid");
  }
  if (
    input.expectedSha256 &&
    (await sha256File(input.sourcePath)) !== input.expectedSha256
  ) {
    throw new Error("workspace_durable_seed_digest_mismatch");
  }
  await fs.copyFile(input.sourcePath, input.targetPath);
}

async function persistDurableSeedArchive(input: {
  sourcePath: string;
  targetPath: string;
}): Promise<void> {
  const parent = path.dirname(input.targetPath);
  await fs.mkdir(parent, { recursive: true, mode: 0o700 });
  const parentStat = await fs.lstat(parent);
  if (parentStat.isSymbolicLink() || !parentStat.isDirectory()) {

View on GitHub (pinned to 01ad858492)

Solutions

  1. 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.
  2. Verify with `ls -la <sourcePath>` / `stat -c '%F'` that the entry is 'regular file'.
  3. If the seed should be a directory, use a directory seed mechanism or an archive file instead.
  4. Re-run prepareSandboxManagedRuntime after fixing the path; the error is raised during runtime preparation, not mid-run.

Example fix

// before: seed config pointing at a symlink
{ sourcePath: "/home/dev/.aws/config", targetPath: "/workspace/.aws/config" }  // .aws/config -> dotfiles/aws-config (symlink)
// after: materialize a real file first
execSync(`cp --remove-destination "$(readlink -f /home/dev/.aws/config)" /home/dev/.aws/config`);
// then seed with the same sourcePath
Defensive patterns

Strategy: validation

Validate before calling

const st = await fs.lstat(seed.sourcePath);
if (st.isSymbolicLink() || !st.isFile()) {
  throw new Error(`durable seed ${seed.sourcePath} must be a regular file (got ${st.isSymbolicLink() ? "symlink" : "other"})`);
}

Type guard

function isRegularFileSync(p: string): boolean {
  try { const st = fs.lstatSync(p); return st.isFile() && !st.isSymbolicLink(); } catch { return false; }
}

Try / catch

try {
  await prepareSandboxManagedRuntime(opts);
} catch (err) {
  if (err.message === "workspace_durable_seed_invalid") {
    console.error(`seed source ${opts.seedSourcePath} is a symlink or not a regular file; materialize it first`);
  } else if (err.message === "workspace_durable_seed_digest_mismatch") {
    console.error("seed file changed; refresh expectedSha256");
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/d1cef8b174e7e8e4. Report an issue: GitHub.