{"record":{"id":"337361d47c258f57","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-needs-visible-current","errorCode":null,"errorMessage":"Operation ${operationNumber} needs visible current text.","messagePattern":"Operation (.+?) needs visible current text\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1566,"sourceCode":"\t\t\tselectionRawOffsets.push(index);\n\t\t} else {\n\t\t\tliteral += character;\n\t\t}\n\t\tindex += character.length;\n\t}\n\tflushLiteral();\n\n\tlet strippedLeading = 0;\n\twhile (tokens[0]?.kind === \"gap\") {\n\t\ttokens.shift();\n\t\tstrippedLeading++;\n\t}\n\twhile (tokens.at(-1)?.kind === \"gap\") tokens.pop();\n\tfor (let index = 0; index < selectionBoundaries.length; index++) {\n\t\tselectionBoundaries[index] = Math.max(0, Math.min(tokens.length, selectionBoundaries[index] - strippedLeading));\n\t}\n\tconst literals = tokens.filter((token): token is LiteralToken => token.kind === \"literal\");\n\tif (literals.length === 0) throw new Error(`Operation ${operationNumber} needs visible current text.`);\n\t// Only punctuation-only anchors (`}`, `};`, `);`) are genuinely too generic:\n\t// they match everywhere and their candidate lists are noise. Any identifier\n\t// text — however short (`id`, `avlue`) — is a legitimate anchor; uniqueness\n\t// (or an explicit `«*`) decides whether it applies, not its length.\n\tconst hasIdentifierText = literals.some(token => /[\\p{L}\\p{N}_$]/u.test(token.normalized));\n\tif (!hasIdentifierText) {\n\t\tthrow new Error(`Operation ${operationNumber} pattern is too generic; include a distinctive name or statement.`);\n\t}\n\n\tconst emptyDoubleSelection = selectionBoundaries.length === 2 && selectionBoundaries[0] === selectionBoundaries[1];\n\tconst insertion = selectionBoundaries.length === 1 || emptyDoubleSelection;\n\tconst explicitSingleSelection = selectionBoundaries.length === 2 && !emptyDoubleSelection;\n\tconst selectionStart = insertion || explicitSingleSelection ? selectionBoundaries[0] : 0;\n\tconst selectionEnd = insertion ? selectionStart : explicitSingleSelection ? selectionBoundaries[1] : tokens.length;\n\tconst selectionPairs =\n\t\tselectionBoundaries.length > 0 && selectionBoundaries.length % 2 === 0\n\t\t\t? Array.from({ length: selectionBoundaries.length / 2 }, (_, index) => {\n\t\t\t\t\tconst start = selectionBoundaries[index * 2];","sourceCodeStart":1548,"sourceCodeEnd":1584,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1548-L1584","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst pattern = \"...\";\n// after\nconst pattern = \"const total = sum(items);\\n...\";","handlingStrategy":"validation","validationCode":"const literalsOnly = pattern.replace(/«|»|\\.\\.\\./g, \"\").trim();\nif (literalsOnly === \"\") throw new Error(\"pattern is gaps-only; add a literal anchor line\");","typeGuard":"function hasLiteralAnchor(pattern: string): boolean {\n  return pattern.replace(/«|»|\\.\\.\\./g, \"\").trim().length > 0;\n}","tryCatchPattern":"try {\n  await applySloppyEdit({ pattern });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"needs visible current text\")) {\n    // insert an anchor line from the target region into the pattern\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["edit-pattern","validation","gap-marker"],"backgroundTag":"empty-edit-pattern","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}