{"record":{"id":"9b90d451dbc8ca2d","repo":"paperclipai/paperclip","slug":"unexpected-attempt-path-join-source-entry-name","errorCode":null,"errorMessage":"Unexpected attempt path ${join(source, entry.name)}","messagePattern":"Unexpected attempt path (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs","lineNumber":243,"sourceCode":"    Object.entries(entries)\n      .map(([key, value]) => `${key}=${value}\\n`)\n      .join(\"\"),\n  );\n}\n\nasync function copyAttempt(source, destination) {\n  const metadata = await lstat(source);\n  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {\n    throw new Error(`Attempt source is not a real directory: ${source}`);\n  }\n  await mkdir(destination, { recursive: false, mode: 0o700 });\n  for (const entry of await readdir(source, { withFileTypes: true })) {\n    if (\n      !entry.isFile() ||\n      entry.isSymbolicLink() ||\n      !ATTEMPT_FILES.has(entry.name)\n    ) {\n      throw new Error(`Unexpected attempt path ${join(source, entry.name)}`);\n    }\n    await cp(join(source, entry.name), join(destination, entry.name), {\n      errorOnExist: true,\n    });\n  }\n  for (const required of [\n    \"artifact.json\",\n    \"score.json\",\n    \"case.json\",\n    \"config.json\",\n  ]) {\n    await lstat(join(destination, required));\n  }\n}\n\nasync function findFiles(root, name) {\n  const metadata = await lstat(root).catch(() => null);\n  if (!metadata) return [];","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/scripts/runner-protocol-eval-campaign.mjs#L225-L261","documentation":"copyAttempt enumerates the source attempt directory and only accepts entries that are real files (not symlinks) whose names are in the ATTEMPT_FILES allowlist. Any subdirectory, symlink, or unexpected file causes an abort so that only known attempt artifacts are copied into the campaign.","triggerScenarios":"The attempt directory contains a file not listed in ATTEMPT_FILES, a subdirectory, or a symlinked entry; leftover logs, editor swap files (e.g. .DS_Store, vim swp), or partial-download files inside the attempt folder.","commonSituations":"A crashed run wrote extra output files into the attempt directory; OS artifact files (.DS_Store) created by browsing; someone added a new artifact file without updating ATTEMPT_FILES; an agent wrote debug output into the attempt folder.","solutions":["Remove the unexpected file/directory from the attempt folder (keep only files named in ATTEMPT_FILES).","If the new file is a legitimate attempt artifact, add its exact name to the ATTEMPT_FILES set in the script.","Clean OS-generated files (.DS_Store, Thumbs.db) before copying."],"exampleFix":"// before\nconst ATTEMPT_FILES = new Set([\"result.json\", \"log.txt\"]);\n// after\nconst ATTEMPT_FILES = new Set([\"result.json\", \"log.txt\", \"trace.json\"]);","handlingStrategy":"validation","validationCode":"const entries = await readdir(source, { withFileTypes: true });\nconst bad = entries.filter((e) => !e.isFile() || e.isSymbolicLink() || !ATTEMPT_FILES.has(e.name));\nif (bad.length > 0) {\n  console.error(\"Unexpected entries:\", bad.map((e) => e.name).join(\", \"));\n}","typeGuard":"function isAllowedAttemptEntry(e) {\n  return e.isFile() && !e.isSymbolicLink() && ATTEMPT_FILES.has(e.name);\n}","tryCatchPattern":"try {\n  await copyAttempt(source, dest);\n} catch (err) {\n  if (err.message.startsWith(\"Unexpected attempt path\")) {\n    console.error(err.message, \"— remove the file or extend ATTEMPT_FILES\");\n    process.exit(1);\n  }\n  throw err;\n}","preventionTips":["Keep the attempt directory free of editor/OS artifacts (.DS_Store, swap files).","When adding a new artifact produced by runs, update ATTEMPT_FILES in the same PR."],"tags":["filesystem","allowlist","validation"],"backgroundTag":"unexpected-file-in-directory","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"}