{"record":{"id":"a7e0bd54a1c91d16","repo":"can1357/oh-my-pi","slug":"invalid-control-line-json-stringify-trimmed-u","errorCode":null,"errorMessage":"Invalid control line ${JSON.stringify(trimmed)}; use only ${OPENER}, ${OPENER}*, ${REWRITE_HEADER}, or ${REWRITE_HEADER}N in REWRITE.","messagePattern":"Invalid control line (.+?); use only (.+?), (.+?)\\*, (.+?), or (.+?)N in REWRITE\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1328,"sourceCode":"\t\tconst trimmed = line.trim();\n\t\tconst registerReference = trimmed.match(/^»([1-9]\\d*)$/u);\n\t\tif (isOrdinalOpener(line)) {\n\t\t\tthrow new Error(\n\t\t\t\t`${trimmed} is not a valid opener. Use ${OPENER} with a pattern that matches once — add context only the intended match has — or ${OPENER}* to change every match.`,\n\t\t\t);\n\t\t}\n\t\tif (trimmed === `${OPENER}${REWRITE_HEADER}`) {\n\t\t\t// A glued «» line: after MATCH content it is a mistyped » separator;\n\t\t\t// anywhere else it is a stray operation terminator to drop.\n\t\t\tif (state === \"pattern\" && patternLines.some(entry => entry.trim() !== \"\")) state = \"rewrite\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\tparsedOpener === false &&\n\t\t\t(trimmed.startsWith(OPENER) ||\n\t\t\t\t(trimmed.startsWith(REWRITE_HEADER) && trimmed !== REWRITE_HEADER && !registerReference))\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid control line ${JSON.stringify(trimmed)}; use only ${OPENER}, ${OPENER}*, ${REWRITE_HEADER}, or ${REWRITE_HEADER}N in REWRITE.`,\n\t\t\t);\n\t\t}\n\t\tif (state === \"outside\") {\n\t\t\tif (parsedOpener !== false) {\n\t\t\t\tallMatches = parsedOpener === 0;\n\t\t\t\tpatternLines = [];\n\t\t\t\trewriteLines = [];\n\t\t\t\treferenceSeparator = undefined;\n\t\t\t\tstate = \"pattern\";\n\t\t\t} else if (trimmed !== \"\") {\n\t\t\t\tthrow new Error(`Expected ${OPENER} on input line ${index + 1}.`);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (state === \"pattern\") {\n\t\t\tconst accumulated = patternLines.join(\"\\n\");","sourceCodeStart":1310,"sourceCodeEnd":1346,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1310-L1346","documentation":"Lines that begin with a control marker (« or ») must be exactly one of the four legal control lines: «, «*, », or »N (a register reference). Anything else starting with those glyphs — e.g. «/path/to/file, « some comment, »! — is rejected so malformed payloads fail loudly instead of being silently parsed as literal text. The offending line is echoed with JSON.stringify for exactness.","triggerScenarios":"A line inside the section body starts with « or » but is not exactly «, «*, », or »[1-9] — e.g. «file.ts headers carried over from other formats, «foo arguments glued to the opener, or «*2 mixed variants.","commonSituations":"Models gluing file paths or parameters onto openers («main.ts); leftover headers from apply-patch-style payloads; typos like «* * or «*2; pasted text containing the guillemet characters mid-payload.","solutions":["Make the line exactly « (unique match), «* (all matches), » (start rewrite), or »N (reference earlier operation) — nothing else on the line.","If you meant a file path header, use the § section opener format (e.g. §path/to/file.ts) instead of gluing it to «.","If the line is literal file content that happens to start with « or », rephrase or re-indent it so the payload is unambiguous."],"exampleFix":"// before\n«main.ts\npattern\n»\nreplacement\n\n// after\n§main.ts\n«\npattern\n»\nreplacement","handlingStrategy":"validation","validationCode":"for (const line of body.split(\"\\n\")) {\n  const t = line.trim();\n  if ((t.startsWith(\"«\") || t.startsWith(\"»\")) && ![\"«\", \"«*\", \"»\"].includes(t) && !/^»[1-9]\\d*$/.test(t)) {\n    throw new Error(`invalid control line: ${t}`);\n  }\n}","typeGuard":"const isLegalControlLine = (t: string): boolean =>\n  t === \"«\" || t === \"«*\" || t === \"»\" || /^»[1-9]\\d*$/.test(t);","tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.startsWith(\"Invalid control line\")) {\n    const bad = JSON.parse(err.message.match(/Invalid control line (\".*?\")/)?.[1] ?? '\"\"');\n    // strip or fix the offending line and retry\n  }\n}","preventionTips":["Put file paths in § section headers, never after «.","Keep control marker lines bare: «, «*, », »N with nothing else.","Escape or re-indent literal content that begins with « or »."],"tags":["parser","edit-format","control-line"],"backgroundTag":"invalid-opener-syntax","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}