{"record":{"id":"c4518ef6904ced2b","repo":"paperclipai/paperclip","slug":"registered-base-project-workspace-paperclip-config","errorCode":null,"errorMessage":"Registered base project workspace Paperclip config at ${configPath} cannot be inspected (${errorCode(error)}${detail ?? \"\"}).","messagePattern":"Registered base project workspace Paperclip config at (.+?) cannot be inspected \\((.+?)(.+?)\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shared/src/worktree-seed-source.ts","lineNumber":62,"sourceCode":"  throw new Error(`Registered ${label} Paperclip config has no PAPERCLIP_INSTANCE_ID binding.`);\n}\n\nfunction errorCode(error: unknown): string {\n  return (error as NodeJS.ErrnoException | null)?.code ?? \"unknown error\";\n}\n\n/**\n * Inspect a directory entry without following it, returning null only when it is absent.\n *\n * Any other failure means the declared path is unreadable or malformed, and a guess there\n * would silently seed from a different instance.\n */\nfunction inspectDeclaredEntry(entryPath: string, configPath: string, detail?: string): Stats | null {\n  try {\n    return lstatSync(entryPath);\n  } catch (error) {\n    if (errorCode(error) === \"ENOENT\") return null;\n    throw new Error(\n      `Registered base project workspace Paperclip config at ${configPath} cannot be inspected (${errorCode(error)}${detail ?? \"\"}).`,\n    );\n  }\n}\n\n/**\n * Whether a base project workspace declares an instance config of its own.\n *\n * This tests directory entries and does not follow them. A dangling or aliased symlink,\n * at the config itself or at the `.paperclip` directory holding it, still counts as a\n * declared config, so the resolver rejects the malformed source instead of falling back\n * to another one.\n */\nexport function baseWorkspaceDeclaresInstanceConfig(baseWorkspaceCwd: string): boolean {\n  const configDir = path.join(baseWorkspaceCwd, \".paperclip\");\n  const configPath = path.join(configDir, \"config.json\");\n  if (inspectDeclaredEntry(configPath, configPath)) return true;\n","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/packages/shared/src/worktree-seed-source.ts#L44-L80","documentation":"Thrown by inspectDeclaredEntry in worktree-seed-source.ts when lstatSync on the declared config path (or the .paperclip directory entry) fails with any code other than ENOENT. Absent entries are fine (returns null); unreadable, malformed, or looping entries (EACCES, EPERM, ENOTDIR, ELOOP, EMFILE) mean the declared source cannot be verified, and the resolver fails closed rather than silently seeding from a different instance.","triggerScenarios":"resolveRegisteredWorktreeSeedSource probes <baseWorkspace>/.paperclip/config.json or the .paperclip entry: a permission-denied home/workspace directory yields EACCES; a path component that is a file yields ENOTDIR; a symlink cycle on .paperclip yields ELOOP; FD exhaustion yields EMFILE.","commonSituations":"Workspaces under directories owned by another user or with restrictive modes (e.g. after chown/restore from archive); .paperclip accidentally created as a file, or a parent of it; self-referential symlinks left by broken dotfile managers; running as a different user/service account than the one that created the workspace.","solutions":["Read the errno in the message: EACCES/EPERM -> fix ownership/permissions on the workspace and every parent (chmod/chown so the process can lstat); ENOTDIR -> a path component is a file, correct the layout; ELOOP -> remove the symlink loop on .paperclip or the config; EMFILE -> raise the FD limit.","Verify with: ls -la <workspace>/.paperclip and namei -l <workspace>/.paperclip/config.json to see where traversal breaks.","Run the command as the user that owns the workspace, or grant that user read+execute (search) permission on the path.","Replace a symlinked .paperclip with a real directory if the link target is missing or cyclic."],"exampleFix":"# before: .paperclip is a symlink loop\nln -s .paperclip .paperclip  # accidental self-link\n\n# after\nrm <workspace>/.paperclip && mkdir <workspace>/.paperclip\n# then re-create or restore config.json inside it","handlingStrategy":"validation","validationCode":"import fs from \"node:fs\";\nfunction workspaceInspectable(workspace: string): boolean {\n  const entries = [workspace, path.join(workspace, \".paperclip\"), path.join(workspace, \".paperclip\", \"config.json\")];\n  try {\n    entries.forEach((p) => fs.lstatSync(p));\n    return true;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  resolveRegisteredWorktreeSeedSource(input);\n} catch (error) {\n  if (error instanceof Error && error.message.includes(\"cannot be inspected\")) {\n    // parse the errno in the message: EACCES -> fix perms/owner; ENOTDIR/ELOOP -> fix layout/symlink; EMFILE -> raise FD limit\n  }\n  throw error;\n}","preventionTips":["Run workspace operations as the user who owns the workspace tree.","Ensure search (execute) permission on every parent of .paperclip/config.json.","Avoid symlinked .paperclip directories, especially dangling or cyclic ones.","After restores/chowns, verify with namei -l <workspace>/.paperclip/config.json."],"tags":["worktree","seed-source","filesystem","permissions","lstat"],"backgroundTag":"filesystem-permission-error","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-21T17:58:32.592Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}