{"record":{"id":"9150e6c866a027a4","repo":"can1357/oh-my-pi","slug":"archive-hard-link-formatarchivepathforerror-ent","errorCode":null,"errorMessage":"Archive hard link '${formatArchivePathForError(entry.path)}' has a cyclic target '${formatArchivePathForError(pending.targetPath)}'","messagePattern":"Archive hard link '(.+?)' has a cyclic target '(.+?)'","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/tar.ts","lineNumber":380,"sourceCode":"\t\t\t\tif (!(error instanceof ArchiveError)) throw new ArchiveError(\"Invalid archive link\");\n\t\t\t}\n\t\t\tif (targetPath !== pending.targetPath) blocker = findUnresolvedBlocker(targetPath);\n\t\t}\n\t\tif (blocker !== null && blocker !== entry) {\n\t\t\tconst waiting = dependents.get(blocker);\n\t\t\tif (waiting) waiting.push(entry);\n\t\t\telse dependents.set(blocker, [entry]);\n\t\t\tcontinue;\n\t\t}\n\t\tunresolved.delete(entry);\n\t\tconst settled = dependents.get(entry);\n\t\tif (settled) {\n\t\t\tdependents.delete(entry);\n\t\t\tqueue.push(...settled);\n\t\t}\n\t\tif (blocker === entry) {\n\t\t\tif (pending.kind === \"hard link\") {\n\t\t\t\tthrow new ArchiveError(\n\t\t\t\t\t`Archive hard link '${formatArchivePathForError(entry.path)}' has a cyclic target '${formatArchivePathForError(pending.targetPath)}'`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tentry.storage = { type: \"link\", targetPath: pending.targetPath, resolveTarget: false };\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = entries.get(targetPath);\n\t\tif (target?.storage && !target.isDirectory && !unresolved.has(target)) {\n\t\t\tentry.size = target.size;\n\t\t\tentry.storage = target.storage;\n\t\t\tcontinue;\n\t\t}\n\t\tconst targetIsDirectory = targetPath === \"\" || target?.isDirectory === true || directoryPrefixes.has(targetPath);\n\t\tif (!targetIsDirectory) {\n\t\t\tif (pending.kind === \"symlink\") {\n\t\t\t\tentry.storage = { type: \"link\", targetPath: pending.targetPath, resolveTarget: false };\n\t\t\t\tcontinue;\n\t\t\t}","sourceCodeStart":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/tar.ts#L362-L398","documentation":"Thrown when a tar hard link's target chain resolves back to the hard-link entry itself, i.e. the link is cyclic (directly or through other links). Hard links must point at a concrete file member; a cycle can never resolve, so indexing aborts.","triggerScenarios":"readTar on an archive containing typeflag-'1' entries where entry A hard-links to B and B (or a chain) links back to A; the pending-link resolver detects blocker === entry for a hard link.","commonSituations":"Maliciously crafted archives (link-cycle bombs), archives corrupted so linkname fields point back at themselves, or round-trips through tools that mishardlink.","solutions":["List the archive's links (`tar tvf`) and locate the self-referential hard-link pair named in the message.","Re-create the archive without the cyclic hard link, replacing it with a regular file copy.","Reject the archive as malicious/corrupt in automated pipelines — catch ArchiveError and fail closed."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Pre-scan hardlink entries for self-referential targets before indexing\n// (entry.path === resolved linkname indicates a cycle)","typeGuard":null,"tryCatchPattern":"try {\n  await readTar(bytes, opts);\n} catch (e) {\n  if (e instanceof ArchiveError && e.message.includes(\"cyclic target\")) {\n    throw new Error(\"Refusing archive: hard-link cycle (possible tampering)\");\n  }\n  throw e;\n}","preventionTips":["Never create hard links whose target chain loops back to the link itself.","Sanitize untrusted archives (e.g. with bsdtar re-pack) before indexing.","Treat hard-link cycles as malicious input and reject outright."],"tags":["archive","tar","hardlink","cycle"],"backgroundTag":"cyclic-archive-link","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}