{"record":{"id":"894cf38253e35a0a","repo":"can1357/oh-my-pi","slug":"unexpected-token-at-position-start","errorCode":null,"errorMessage":"Unexpected token at position ${start}","messagePattern":"Unexpected token at position (.+?)","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/json-parse.ts","lineNumber":532,"sourceCode":"\t *   unquoted keys, so a missed comma (`{\"a\": foo \"b\": 1}`, `{a: foo b: 1}`)\n\t *   would otherwise silently swallow the following field. A colon followed\n\t *   by `/` or `\\` stays literal so URL and Windows-path values recover;\n\t * - is a non-finite atom ({@link NON_RECOVERABLE_BAREWORDS}).\n\t */\n\t#bareword(): string {\n\t\tconst s = this.#s;\n\t\tconst start = this.#i;\n\t\tlet i = start;\n\t\twhile (i < this.#n) {\n\t\t\tconst cc = s.charCodeAt(i);\n\t\t\tif (cc === 0x2c /* , */ || cc === 0x7d /* } */ || cc === 0x5d /* ] */ || cc === 0x0a || cc === 0x0d) break;\n\t\t\tif (\n\t\t\t\tcc === QUOTE ||\n\t\t\t\tcc === 0x7b /* { */ ||\n\t\t\t\tcc === 0x5b /* [ */ ||\n\t\t\t\t(cc === 0x3a /* : */ && s.charCodeAt(i + 1) !== 0x2f /* / */ && s.charCodeAt(i + 1) !== 0x5c) /* \\ */\n\t\t\t) {\n\t\t\t\tthrow new SyntaxError(`Unexpected token at position ${start}`);\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t\tif (i >= this.#n) throw new SyntaxError(`Unexpected token at position ${start}`);\n\t\tlet end = i;\n\t\twhile (end > start && isWhitespace(s.charCodeAt(end - 1))) end--;\n\t\tconst word = s.slice(start, end);\n\t\tif (NON_RECOVERABLE_BAREWORDS[word]) throw new SyntaxError(`Unexpected token at position ${start}`);\n\t\tthis.#i = i;\n\t\treturn word;\n\t}\n}\n\n/**\n * Final-parse a JSON value, repairing the common LLM malformations\n * ({@link RelaxedJson}). Tries strict `JSON.parse` first (fast path, exact JSON\n * semantics), then the relaxed parser. Throws when the input is unrepairable,\n * truncated, or carries trailing garbage — so callers can skip a bad tool call","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/json-parse.ts#L514-L550","documentation":"Thrown inside #bareword during strict-mode recovery of an unquoted string value: while scanning the bareword, the scanner hit a character that is forbidden inside a recoverable bareword — a quote, '{', '[', or a ':' not followed by '/' or '\\\\'. Such a character means the text is not a plausible unquoted string (it looks like real JSON structure or a quoted value), so recovery aborts rather than guessing. The position reported is where the bareword started.","triggerScenarios":"Calling parse with an unquoted value that contains structural characters, e.g. '{\"a\": [1,2]}' as a bareword value ( '[' inside the word), '{\"a\": \"x\"}' missing the opening quote handling (a quote mid-scan), or '{\"a\": b:c}' (':' without a following '/' or '\\\\'). Also thrown at the second site when the scan runs past end-of-input before finding a terminator.","commonSituations":"Recovery attempts on partially-corrupt JSON produced by LLM output, template substitutions injecting unquoted values containing JSON syntax, unquoted globs/paths like packages/* which are fine but windows paths C:\\\\... vs URLs http:// which interact with the ':' rule, and truncated files (scan hits EOF).","solutions":["Quote the offending value: {\"a\": \"[1,2]\"} instead of {\"a\": [1,2]} where you meant a string.","Check the character at the reported position — quotes/brackets/colons cannot appear in an unquoted bareword.","If input is truncated, supply the rest of the input; the parser cannot recover a bareword that never terminates.","Use proper JSON (run it through a JSON formatter) instead of relying on the recovery path."],"exampleFix":"// before\nparse('{\"pattern\": \"*.ts\"}'); // missing colon handling in bareword → SyntaxError at start\nparse('{\"x\": [1,2]}'); // intended as string\n// after\nparse('{\"x\": \"[1,2]\"}'); // quote values containing JSON structural characters","handlingStrategy":"validation","validationCode":"// reject unquoted values containing structural characters before recovery parse\nconst badUnquoted = /(^|[\\[,:]\\s*)(?![\"\\d{\\[tfn\\-])([^\\s,\"}\\]]*[\"\\[{:]|.*:\\S)/;\nif (badUnquoted.test(input)) {\n  throw new SyntaxError(\"Unquoted value contains structural characters; quote it\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  return parse(input);\n} catch (err) {\n  if (err instanceof SyntaxError && err.message.startsWith(\"Unexpected token at position\")) {\n    const start = Number(err.message.match(/position (\\d+)/)?.[1]);\n    throw new SyntaxError(\n      `Unquoted string at ${start} contains quote/bracket/colon — quote the value`,\n    );\n  }\n  throw err;\n}","preventionTips":["Quote any value containing \", {, [, or : — bareword recovery only handles plain strings/globs/paths.","Remember barewords cannot contain ':' unless followed by / or \\\\ (URLs like http:// are OK; 'a:b' is not).","Prefer emitting fully valid JSON from producers instead of relying on recovery."],"tags":["json","syntax-error","bareword","recovery"],"backgroundTag":"invalid-json-syntax","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}