can1357/oh-my-pi · error · Error

unseenLinesMessage(section.path, unseen, expected, { lines:

Error message

unseenLinesMessage(section.path, unseen, expected, { lines: revealed, truncated })

What it means

#assertSeenLines enforces that every line a hashline patch will edit was actually revealed to the caller in a prior read. If some expected lines were never seen, it throws unseenLinesMessage listing the section, unseen lines, and what was revealed, preventing blind edits against unobserved content. If the reveal was column-truncated, seen lines are not credited, forcing a full-range re-read.

Source

Thrown at packages/hashline/src/patcher.ts:653

			if (line < 1 || line > sourceLines.length) continue;
			const source = sourceLines[line - 1] ?? "";
			if (source.length > SEEN_LINE_REVEAL_MAX_COLUMNS) {
				revealed.push({ line, text: `${source.slice(0, SEEN_LINE_REVEAL_MAX_COLUMNS)}…` });
				columnTruncated = true;
			} else {
				revealed.push({ line, text: source });
			}
		}
		const truncated = unseen.length > revealed.length || columnTruncated;
		// Only merge when the reveal covered every unseen anchor line in full
		// width. A prefix-truncated reveal would let the model split a blind
		// edit into <=cap-line retries and land it without ever running the
		// required range re-read; a column-clipped reveal would leave part of
		// each line unseen while the model receives an "ok to retry" signal.
		if (!truncated) {
			for (const { line } of revealed) seen.add(line);
		}
		throw new Error(unseenLinesMessage(section.path, unseen, expected, { lines: revealed, truncated }));
	}
	#mismatchError(
		section: PatchSection,
		canonicalPath: string,
		normalized: string,
		expected: string,
		hashRecognized: boolean,
	): MismatchError {
		const actualFileHash = this.#recordFullSnapshot(canonicalPath, normalized);
		return new MismatchError({
			path: section.path,
			expectedFileHash: expected,
			actualFileHash,
			fileLines: normalized.split("\n"),
			anchorLines: section.collectAnchorLines(),
			hashRecognized,
		});
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-read the target section (full lines, within the column cap) before retrying the edit
  2. Retry the edit so the re-read marks the lines as seen, then re-issue the patch
  3. Avoid clipped/truncated reads of the lines you intend to edit
  4. If the file changed externally, restart the read-edit cycle from a fresh read

Example fix

// before
applyPatch(edits); // lines 40-45 never read → throws
// after
const section = readSection(path, 40, 45); // full, untruncated read
applyPatch(edits); // lines now marked seen
Defensive patterns

Strategy: try-catch

Try / catch

try {
  applyPatch(edits);
} catch (e) {
  if (e.message.includes("were never read") || /unseen/i.test(e.message)) {
    await readSection(path, startLine, endLine); // full, untruncated read
    applyPatch(edits); // retry after lines are marked seen
  }
}

Prevention

When it happens

Trigger: Applying a hashline patch to lines that were never included in a prior read of the section, or where prior reads were truncated (row cap or column clipping) so the patcher cannot confirm those lines were seen.

Common situations: An agent retrying an edit without re-reading the target region, read results capped by line/width limits, editing a different section of a file after only reading part of it, or stale session state after the file changed on disk.

Related errors


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