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
- Add at least one literal line of code from the target region as an anchor.
- If you want to match from a point to the end, keep a leading anchor literal before the `...` gap.
- 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
- Every pattern needs at least one concrete line of code outside the gaps.
- Anchor the region with its enclosing declaration before relying on gaps.
- In generated patterns, assert at least one literal line survives templating.
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
- Operation ${operationNumber} has adjacent ${GAP}; use one el
- Operation ${operationNumber} has no visible current text.
- Operation ${operationNumber} pattern is too generic; include
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/337361d47c258f57.
Report an issue: GitHub.