{"record":{"id":"384320760b11d34e","repo":"can1357/oh-my-pi","slug":"replace-memory-files-contains-duplicate-name","errorCode":null,"errorMessage":"replace_memory_files contains duplicate ${name}","messagePattern":"replace_memory_files contains duplicate (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/sharpshooter/consolidate.ts","lineNumber":254,"sourceCode":"\t}\n\n\tconst args = toolCalls[0].arguments;\n\tif (!args || typeof args !== \"object\" || !(\"files\" in args) || !Array.isArray(args.files)) {\n\t\tthrow new Error(\"replace_memory_files requires a files array\");\n\t}\n\n\tconst seen = new Set<SharpshooterMemoryFile>();\n\tconst files: ReplacementFile[] = [];\n\tfor (const item of args.files) {\n\t\tif (!item || typeof item !== \"object\" || !(\"name\" in item) || !(\"content\" in item)) {\n\t\t\tthrow new Error(\"replace_memory_files contains an invalid file entry\");\n\t\t}\n\t\tconst name = item.name;\n\t\tconst rawContent = item.content;\n\t\tif (!isMemoryFileName(name) || typeof rawContent !== \"string\") {\n\t\t\tthrow new Error(\"replace_memory_files contains an invalid file entry\");\n\t\t}\n\t\tif (seen.has(name)) throw new Error(`replace_memory_files contains duplicate ${name}`);\n\t\tseen.add(name);\n\t\tconst redacted = redactSecrets(rawContent);\n\t\tlet lines = redacted.length > 0 ? 1 : 0;\n\t\tfor (let index = 0; index + 1 < redacted.length; index++) {\n\t\t\tif (redacted.charCodeAt(index) === 10) lines += 1;\n\t\t}\n\t\tif (lines > SHARPSHOOTER_MAX_FILE_LINES) {\n\t\t\tthrow new Error(`${name} exceeds the ${SHARPSHOOTER_MAX_FILE_LINES}-line limit`);\n\t\t}\n\t\tfiles.push({ name, content: redacted });\n\t}\n\tconst totalChars = files.reduce((sum, file) => sum + file.content.trim().length, 0);\n\tif (totalChars === 0 && SHARPSHOOTER_MEMORY_FILES.some(name => currentFiles[name].trim().length > 0)) {\n\t\tthrow new Error(\"replace_memory_files returned all-empty content; refusing to wipe memory files\");\n\t}\n\treturn files;\n}\n","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/sharpshooter/consolidate.ts#L236-L272","documentation":"The `files` array must list each allowed memory file at most once. This error is thrown when a second entry repeats an already-seen valid memory file name, preventing ambiguous double-replacement.","triggerScenarios":"The model emits the same memory filename twice in one `replace_memory_files` call, e.g. two `AGENTS.md` entries with different content.","commonSituations":"Models regenerating a file after 'remembering' an earlier draft in the same call; streaming dedup failures; template prompts encouraging per-section entries with the same name.","solutions":["Merge duplicate entries into one `content` string per file name.","Keep only the final intended version of each file and drop earlier drafts.","Retry the run if the duplicates came from flaky model output.","If generating calls in code, dedupe by name before invoking."],"exampleFix":"// before\n{ files: [{name:\"A.md\",content:\"x\"},{name:\"A.md\",content:\"y\"}] }\n// after\n{ files: [{ name: \"A.md\", content: \"x\\ny\" }] }","handlingStrategy":"validation","validationCode":"const names = files.map(f => f?.name);\nif (new Set(names).size !== names.length) throw new Error(\"duplicate memory file names\");","typeGuard":"null","tryCatchPattern":"try {\n  const files = parseReplacementFiles(call, currentFiles);\n} catch (err) {\n  const m = String(err).match(/duplicate (.+)$/);\n  if (m) logger.warn(`duplicate entry for ${m[1]}; retrying`);\n}","preventionTips":["Instruct the model: one entry per memory file.","Dedupe by name in any code path that constructs the call.","Prefer 'last entry wins' merge helpers in tests instead of emitting duplicates."],"tags":["validation","deduplication","tool-calling"],"backgroundTag":"duplicate-identifier","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}