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
- Copy the exact `@@@ <path>:<tag>` header from the latest read/grep output and re-issue the edit.
- If the file is new/empty, use the write tool instead of the edit tool.
- 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
- Always build headers by copying the exact `@@@ path:tag` line from the latest read/search result.
- Use the write tool for new files, never edit.
- Re-read after any successful edit — tags change with content.
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
- Multiple hashline sections resolve to the same file (${previ
- oldText must not be empty.
- Found ${occurrences} occurrences${pathSuffix}${moreMsg}:\n\n
- ${message} || Unable to read ${sectionPath}
- No hashline sections found in input.
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/e25d2f41b6572808.
Report an issue: GitHub.