can1357/oh-my-pi · error

Operation ${operationNumber} needs visible current text.

Error message

Operation ${operationNumber} needs visible current text.

What it means

After tokenizing the pattern and stripping leading/trailing gaps, no literal tokens remain — the pattern is made entirely of gap markers (and selections). A pattern with no visible text cannot anchor to any location in the file, so the library throws instead of matching arbitrary content.

Source

Thrown at packages/coding-agent/src/edit/sloppy.ts:1566

			selectionRawOffsets.push(index);
		} else {
			literal += character;
		}
		index += character.length;
	}
	flushLiteral();

	let strippedLeading = 0;
	while (tokens[0]?.kind === "gap") {
		tokens.shift();
		strippedLeading++;
	}
	while (tokens.at(-1)?.kind === "gap") tokens.pop();
	for (let index = 0; index < selectionBoundaries.length; index++) {
		selectionBoundaries[index] = Math.max(0, Math.min(tokens.length, selectionBoundaries[index] - strippedLeading));
	}
	const literals = tokens.filter((token): token is LiteralToken => token.kind === "literal");
	if (literals.length === 0) throw new Error(`Operation ${operationNumber} needs visible current text.`);
	// Only punctuation-only anchors (`}`, `};`, `);`) are genuinely too generic:
	// they match everywhere and their candidate lists are noise. Any identifier
	// text — however short (`id`, `avlue`) — is a legitimate anchor; uniqueness
	// (or an explicit `«*`) decides whether it applies, not its length.
	const hasIdentifierText = literals.some(token => /[\p{L}\p{N}_$]/u.test(token.normalized));
	if (!hasIdentifierText) {
		throw new Error(`Operation ${operationNumber} pattern is too generic; include a distinctive name or statement.`);
	}

	const emptyDoubleSelection = selectionBoundaries.length === 2 && selectionBoundaries[0] === selectionBoundaries[1];
	const insertion = selectionBoundaries.length === 1 || emptyDoubleSelection;
	const explicitSingleSelection = selectionBoundaries.length === 2 && !emptyDoubleSelection;
	const selectionStart = insertion || explicitSingleSelection ? selectionBoundaries[0] : 0;
	const selectionEnd = insertion ? selectionStart : explicitSingleSelection ? selectionBoundaries[1] : tokens.length;
	const selectionPairs =
		selectionBoundaries.length > 0 && selectionBoundaries.length % 2 === 0
			? Array.from({ length: selectionBoundaries.length / 2 }, (_, index) => {
					const start = selectionBoundaries[index * 2];

View on GitHub (pinned to 9690622007)

Solutions

  1. Add at least one literal line of code from the target region as an anchor.
  2. If you want to match from a point to the end, keep a leading anchor literal before the `...` gap.
  3. For pure insertion between known lines, include both surrounding lines plus the gap.

Example fix

// before
const pattern = "...";
// after
const pattern = "const total = sum(items);\n...";
Defensive patterns

Strategy: validation

Validate before calling

const literalsOnly = pattern.replace(/«|»|\.\.\./g, "").trim();
if (literalsOnly === "") throw new Error("pattern is gaps-only; add a literal anchor line");

Type guard

function hasLiteralAnchor(pattern: string): boolean {
  return pattern.replace(/«|»|\.\.\./g, "").trim().length > 0;
}

Try / catch

try {
  await applySloppyEdit({ pattern });
} catch (err) {
  if (err instanceof Error && err.message.includes("needs visible current text")) {
    // insert an anchor line from the target region into the pattern
  }
  throw err;
}

Prevention

When it happens

Trigger: A pattern consisting solely of `...` (with optional selection markers), or one where every line was inside gaps. Detected in the compiled-pattern builder when `tokens.filter(kind === "literal")` is empty after trailing gaps are popped.

Common situations: Trying to 'replace everything between anchors' by writing only gaps and forgetting at least one concrete anchor line; a template that interpolates an empty extracted snippet into the pattern; deleting all the literal lines from a previously working pattern during refactoring.

Related errors


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