{"record":{"id":"b2fb075741d8c89d","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-has-an-empty-pattern","errorCode":null,"errorMessage":"Operation ${operationNumber} has an empty pattern.","messagePattern":"Operation (.+?) has an empty pattern\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1479,"sourceCode":"\t\t\t\tstarts.push(index);\n\t\t\t\tends.push(next);\n\t\t\t}\n\t\t}\n\t\tindex = next;\n\t}\n\treturn { text, starts, ends };\n}\n\nfunction patternGapAt(source: string, offset: number): string | undefined {\n\treturn source.startsWith(GAP, offset) ? GAP : undefined;\n}\n\nfunction patternContainsGap(source: string): boolean {\n\treturn source.includes(GAP);\n}\n\nfunction parsePattern(pattern: string, operationNumber: number): ParsedPattern {\n\tif (pattern.trim() === \"\") throw new Error(`Operation ${operationNumber} has an empty pattern.`);\n\tvalidateSelectionMarkers(pattern, operationNumber);\n\tconst hasGap = patternContainsGap(pattern);\n\tconst hasSelection = pattern.includes(SELECT_OPEN);\n\tif (!hasGap && !hasSelection) {\n\t\tconst normalized = normalizeText(pattern).text;\n\t\tif (normalized === \"\") throw new Error(`Operation ${operationNumber} has no visible current text.`);\n\t\treturn {\n\t\t\ttokens: [{ kind: \"literal\", text: pattern, normalized }],\n\t\t\tselectionStart: 0,\n\t\t\tselectionEnd: 1,\n\t\t\tinsertion: false,\n\t\t\tlineInsertion: false,\n\t\t\tselectedCaptureIndices: [],\n\t\t\tselectionRanges: [],\n\t\t\tselectionPairs: [],\n\t\t\tliteralFallback: undefined,\n\t\t};\n\t}","sourceCodeStart":1461,"sourceCodeEnd":1497,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1461-L1497","documentation":"parsePattern validates each operation's MATCH text. An operation whose pattern is blank (only whitespace) after the « opener carries no information about what to replace, so it is rejected with the operation number. A sibling error (\"has no visible current text\") covers patterns whose characters are all stripped by normalization. validateSelectionMarkers then checks ⟪...⟫ selection markers.","triggerScenarios":"A payload with « followed immediately by » (empty match), or a match block containing only spaces/tabs/newlines; also occurs when a model emits the opener and separator back-to-back intending an insertion-only edit.","commonSituations":"Models attempting pure insertions with an empty MATCH block; truncated payloads where pattern lines were lost; whitespace-only bodies after copy-paste; using this format for file creation instead of the proper add mechanism.","solutions":["Put the exact current text to be replaced between « and » — the pattern must have non-whitespace content.","For an insertion, include the anchor line before the insertion point as the pattern and emit anchor+new text in the rewrite.","Check the payload wasn't truncated or stripped of its pattern lines between generation and apply."],"exampleFix":"// before\n«\n»\nnew text\n\n// after\n«\nanchor line\n»\nanchor line\nnew text","handlingStrategy":"validation","validationCode":"// per-operation: pattern must contain visible text\nconst ops = body.split(/^«\\*?$/m).slice(1);\nfor (let i = 0; i < ops.length; i++) {\n  const pattern = ops[i].split(/^»/m)[0];\n  if (pattern.trim() === \"\") throw new Error(`operation ${i + 1} has an empty pattern`);\n}","typeGuard":"const hasVisiblePattern = (op: string): boolean => op.split(/^»/m)[0].trim() !== \"\";","tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.includes(\"has an empty pattern\")) {\n    // regenerate the operation with an anchored match block\n  }\n}","preventionTips":["Never emit « directly followed by » — every operation needs current text to match.","Model insertions as pattern = anchor line, rewrite = anchor + inserted text.","Use this format only for edits to existing files; file creation belongs to the add-file section mechanism."],"tags":["parser","edit-format","empty-pattern"],"backgroundTag":"empty-payload","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}