can1357/oh-my-pi · error

Missing hashline snapshot tag for ${sectionPath}; use `${HL_

Error message

Missing hashline snapshot tag for ${sectionPath}; use `${HL_FILE_PREFIX}${sectionPath}${HL_FILE_HASH_SEP}tag${HL_FILE_SUFFIX}` from your latest read/search output. To create a new file, use the write tool.

What it means

Hashline edits carry a 4-hex content snapshot tag in the section header proving the model read the current file version. applyPreviewEdits()/apply() throws this message when the header has no tag (fileHash undefined) and hash validation is not skipped — the library refuses to apply edits against an unverified snapshot. The message points at the exact header syntax required and suggests the write tool for new files.

Source

Thrown at packages/coding-agent/src/edit/hashline/diff.ts:215

	if (!hasBlockEdit(edits)) return edits;
	const baseText = expected === undefined || liveMatches ? normalized : snapshots.byHash(absolutePath, expected)?.text;
	if (baseText === undefined) {
		throw createMismatchError(section, absolutePath, normalized, snapshots, expected ?? "");
	}
	return resolveBlockEdits(edits, baseText, section.path, nativeBlockResolver, { onUnresolved: "throw" });
}

function applyPreviewEdits(args: {
	section: PatchSection;
	absolutePath: string;
	normalized: string;
	snapshots: SnapshotStore;
	options: HashlineDiffOptions;
}): ApplyResult {
	const { section, absolutePath, normalized, snapshots, options } = args;
	const expected = section.fileHash;
	if (!options.skipHashValidation && expected === undefined) {
		throw new Error(missingSnapshotTagMessage(section.path));
	}
	// The 4-hex tag is content-derived: when the live text hashes to it, trust
	// the match and preview directly (mirrors Patcher's apply-time behavior).
	const liveMatches = expected !== undefined && computeFileHash(normalized) === expected;
	const edits = parsePreviewEdits(section, options.streaming);
	const resolved = resolvePreviewEdits({ section, absolutePath, normalized, snapshots, expected, liveMatches, edits });
	const clipboard = options.clipboard ?? {};
	// Mirror the Patcher: surface clipboard sequencing mistakes with their
	// targeted message before the recovery path below swallows them. Streaming
	// previews stay lenient — a mid-typed op transiently violating sequencing
	// must not flash an error frame.
	if (!options.streaming) validateClipboardSequence(resolved, clipboard);
	const applyOptions = {
		clipboard,
		path: absolutePath,
		...(options.streaming ? { onEmptyPaste: "drop" as const } : {}),
	};
	if (options.skipHashValidation || expected === undefined || liveMatches) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Copy the exact `@@@ <path>:<tag>` header from the latest read/grep output and re-issue the edit.
  2. If the file is new/empty, use the write tool instead of the edit tool.
  3. Confirm the tag separator (:tag before the closing suffix) survived into the emitted section — parser mismatches surface as 'no tag'.

Example fix

// before
`@@@ src/utils/diff.ts\n- old line\n+ new line`
// after
`@@@ src/utils/diff.ts:9f3c\n- old line\n+ new line`  // tag copied from latest read output
Defensive patterns

Strategy: validation

Validate before calling

// extract tag from latest read output and require it in the header
const header = `@@@ ${path}:${latestSnapshotTag}`;
if (!/:[0-9a-f]{4}/.test(header)) {
  throw new Error(`Refusing to edit: missing snapshot tag in header '${header}'`);
}

Type guard

function hasSnapshotTag(section: string): boolean {
  const header = section.split('\n', 1)[0] ?? '';
  return /:[0-9a-f]{4}\s*$/.test(header);
}

Try / catch

try {
  return await hashlineApply(section);
} catch (e) {
  if (e instanceof Error && e.message.includes('Missing hashline snapshot tag')) {
    const fresh = await readForTag(path);
    return await hashlineApply(section.replace(/^@@@ \S+/, `@@@ ${path}:${fresh.tag}`));
  }
  throw e;
}

Prevention

When it happens

Trigger: Issuing a hashline edit whose header is `@@@ path` with no `:tag` suffix, using a tag format the parser did not recognize, or hand-authoring hashline sections without copying the tag from read/search output.

Common situations: Agents omitting the tag when writing edits from memory, models paraphrasing the header format, editing a file that was never read in the session, or trying to create a new file via edit instead of write.

Related errors


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