{"record":{"id":"3d0995a23532514e","repo":"can1357/oh-my-pi","slug":"duplicate-tar-member-path-formatarchivepathfore","errorCode":null,"errorMessage":"Duplicate tar member path '${formatArchivePathForError(normalized)}'","messagePattern":"Duplicate tar member path '(.+?)'","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/tar.ts","lineNumber":764,"sourceCode":"\t\tconst pathBytes = TEXT_ENCODER.encode(normalized);\n\t\tif (pathBytes.byteLength === 0) throw new ArchiveError(\"Invalid empty tar member path\");\n\t\tif (directory && bytes.byteLength !== 0) {\n\t\t\tthrow new ArchiveError(`Tar directory '${formatArchivePathForError(normalized)}' cannot contain file data`);\n\t\t}\n\t\tconst segments = normalized.split(\"/\");\n\t\tfor (let index = 1; index < segments.length; index++) {\n\t\t\tconst parent = segments.slice(0, index).join(\"/\");\n\t\t\tconst kind = kinds.get(parent);\n\t\t\tif (kind === \"file\")\n\t\t\t\tthrow new ArchiveError(`Tar member '${formatArchivePathForError(parent)}' is not a directory`);\n\t\t\tif (kind === \"directory\") continue;\n\t\t\tkinds.set(parent, \"directory\");\n\t\t\tappendTarEntry(parts, parent, new Uint8Array(0), true, sequence++);\n\t\t}\n\t\tconst existing = kinds.get(normalized);\n\t\tif (existing) {\n\t\t\tif (directory && existing === \"directory\") continue;\n\t\t\tthrow new ArchiveError(`Duplicate tar member path '${formatArchivePathForError(normalized)}'`);\n\t\t}\n\t\tkinds.set(normalized, directory ? \"directory\" : \"file\");\n\t\tappendTarEntry(parts, normalized, bytes, directory, sequence++);\n\t}\n\tparts.push(new Uint8Array(BLOCK_SIZE * 2));\n\treturn concatBytes(parts);\n}\n","sourceCodeStart":746,"sourceCodeEnd":772,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/tar.ts#L746-L772","documentation":"The members map resolved to the same normalized path twice with incompatible intent (two files, a file and a directory, or a duplicate directory that isn't idempotent-mergeable). Tar archives cannot contain two members with the same name, so the encoder throws with the formatted path.","triggerScenarios":"Two distinct raw keys normalize to the same path (e.g. './x' and 'x', or 'a//b' and 'a/b'); a directory entry passed twice is tolerated, but any file duplication or dir/file clash throws.","commonSituations":"Case-insensitive or backslash-sourced listings collapsing to one key; merging archives; a zip importer yielding './' prefixed names.","solutions":["Normalize keys yourself and deduplicate the members map before createTar","Merge duplicate entries by choosing or concatenating content upstream","Use unique, already-normalized relative paths as keys"],"exampleFix":"// before\nmembers.set(\"./x\", a);\nmembers.set(\"x\", b); // normalizes to same path\n// after\nmembers.set(\"x\", chooseContent(a, b));","handlingStrategy":"validation","validationCode":"const seen = new Set<string>();\nfor (const key of members.keys()) {\n\tconst norm = key.replace(/^\\.\\//, \"\").replace(/\\/{2,}/g, \"/\");\n\tif (seen.has(norm)) throw new Error(`Duplicate member path after normalization: '${norm}'`);\n\tseen.add(norm);\n}","typeGuard":null,"tryCatchPattern":"try {\n\treturn await createTar(members);\n} catch (err) {\n\tif (err instanceof ArchiveError && err.message.startsWith(\"Duplicate tar member path\")) {\n\t\t// merge or drop duplicates before retrying\n\t}\n\tthrow err;\n}","preventionTips":["Normalize keys ('./x' → 'x', collapse '//' ) and dedupe before createTar","Use a Map keyed by normalized path so duplicates collapse by construction"],"tags":["archive","tar","path-collision"],"backgroundTag":"archive-member-path-collision","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}