{"record":{"id":"e25d2f41b6572808","repo":"can1357/oh-my-pi","slug":"missing-hashline-snapshot-tag-for-sectionpath","errorCode":null,"errorMessage":"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.","messagePattern":"Missing hashline snapshot tag for (.+?); use `(.+?)(.+?)(.+?)tag(.+?)` from your latest read/search output\\. To create a new file, use the write tool\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/hashline/diff.ts","lineNumber":215,"sourceCode":"\tif (!hasBlockEdit(edits)) return edits;\n\tconst baseText = expected === undefined || liveMatches ? normalized : snapshots.byHash(absolutePath, expected)?.text;\n\tif (baseText === undefined) {\n\t\tthrow createMismatchError(section, absolutePath, normalized, snapshots, expected ?? \"\");\n\t}\n\treturn resolveBlockEdits(edits, baseText, section.path, nativeBlockResolver, { onUnresolved: \"throw\" });\n}\n\nfunction applyPreviewEdits(args: {\n\tsection: PatchSection;\n\tabsolutePath: string;\n\tnormalized: string;\n\tsnapshots: SnapshotStore;\n\toptions: HashlineDiffOptions;\n}): ApplyResult {\n\tconst { section, absolutePath, normalized, snapshots, options } = args;\n\tconst expected = section.fileHash;\n\tif (!options.skipHashValidation && expected === undefined) {\n\t\tthrow new Error(missingSnapshotTagMessage(section.path));\n\t}\n\t// The 4-hex tag is content-derived: when the live text hashes to it, trust\n\t// the match and preview directly (mirrors Patcher's apply-time behavior).\n\tconst liveMatches = expected !== undefined && computeFileHash(normalized) === expected;\n\tconst edits = parsePreviewEdits(section, options.streaming);\n\tconst resolved = resolvePreviewEdits({ section, absolutePath, normalized, snapshots, expected, liveMatches, edits });\n\tconst clipboard = options.clipboard ?? {};\n\t// Mirror the Patcher: surface clipboard sequencing mistakes with their\n\t// targeted message before the recovery path below swallows them. Streaming\n\t// previews stay lenient — a mid-typed op transiently violating sequencing\n\t// must not flash an error frame.\n\tif (!options.streaming) validateClipboardSequence(resolved, clipboard);\n\tconst applyOptions = {\n\t\tclipboard,\n\t\tpath: absolutePath,\n\t\t...(options.streaming ? { onEmptyPaste: \"drop\" as const } : {}),\n\t};\n\tif (options.skipHashValidation || expected === undefined || liveMatches) {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/hashline/diff.ts#L197-L233","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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'."],"exampleFix":"// before\n`@@@ src/utils/diff.ts\\n- old line\\n+ new line`\n// after\n`@@@ src/utils/diff.ts:9f3c\\n- old line\\n+ new line`  // tag copied from latest read output","handlingStrategy":"validation","validationCode":"// extract tag from latest read output and require it in the header\nconst header = `@@@ ${path}:${latestSnapshotTag}`;\nif (!/:[0-9a-f]{4}/.test(header)) {\n  throw new Error(`Refusing to edit: missing snapshot tag in header '${header}'`);\n}","typeGuard":"function hasSnapshotTag(section: string): boolean {\n  const header = section.split('\\n', 1)[0] ?? '';\n  return /:[0-9a-f]{4}\\s*$/.test(header);\n}","tryCatchPattern":"try {\n  return await hashlineApply(section);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Missing hashline snapshot tag')) {\n    const fresh = await readForTag(path);\n    return await hashlineApply(section.replace(/^@@@ \\S+/, `@@@ ${path}:${fresh.tag}`));\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["hashline","validation","missing-snapshot","edit-tool"],"backgroundTag":"missing-hashline-snapshot-tag","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}