can1357/oh-my-pi · error

replace_memory_files contains duplicate ${name}

Error message

replace_memory_files contains duplicate ${name}

What it means

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.

Source

Thrown at packages/coding-agent/src/sharpshooter/consolidate.ts:254

	}

	const args = toolCalls[0].arguments;
	if (!args || typeof args !== "object" || !("files" in args) || !Array.isArray(args.files)) {
		throw new Error("replace_memory_files requires a files array");
	}

	const seen = new Set<SharpshooterMemoryFile>();
	const files: ReplacementFile[] = [];
	for (const item of args.files) {
		if (!item || typeof item !== "object" || !("name" in item) || !("content" in item)) {
			throw new Error("replace_memory_files contains an invalid file entry");
		}
		const name = item.name;
		const rawContent = item.content;
		if (!isMemoryFileName(name) || typeof rawContent !== "string") {
			throw new Error("replace_memory_files contains an invalid file entry");
		}
		if (seen.has(name)) throw new Error(`replace_memory_files contains duplicate ${name}`);
		seen.add(name);
		const redacted = redactSecrets(rawContent);
		let lines = redacted.length > 0 ? 1 : 0;
		for (let index = 0; index + 1 < redacted.length; index++) {
			if (redacted.charCodeAt(index) === 10) lines += 1;
		}
		if (lines > SHARPSHOOTER_MAX_FILE_LINES) {
			throw new Error(`${name} exceeds the ${SHARPSHOOTER_MAX_FILE_LINES}-line limit`);
		}
		files.push({ name, content: redacted });
	}
	const totalChars = files.reduce((sum, file) => sum + file.content.trim().length, 0);
	if (totalChars === 0 && SHARPSHOOTER_MEMORY_FILES.some(name => currentFiles[name].trim().length > 0)) {
		throw new Error("replace_memory_files returned all-empty content; refusing to wipe memory files");
	}
	return files;
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Merge duplicate entries into one `content` string per file name.
  2. Keep only the final intended version of each file and drop earlier drafts.
  3. Retry the run if the duplicates came from flaky model output.
  4. If generating calls in code, dedupe by name before invoking.

Example fix

// before
{ files: [{name:"A.md",content:"x"},{name:"A.md",content:"y"}] }
// after
{ files: [{ name: "A.md", content: "x\ny" }] }
Defensive patterns

Strategy: validation

Validate before calling

const names = files.map(f => f?.name);
if (new Set(names).size !== names.length) throw new Error("duplicate memory file names");

Type guard

null

Try / catch

try {
  const files = parseReplacementFiles(call, currentFiles);
} catch (err) {
  const m = String(err).match(/duplicate (.+)$/);
  if (m) logger.warn(`duplicate entry for ${m[1]}; retrying`);
}

Prevention

When it happens

Trigger: The model emits the same memory filename twice in one `replace_memory_files` call, e.g. two `AGENTS.md` entries with different content.

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/384320760b11d34e. Report an issue: GitHub.