{"record":{"id":"e1844da822fec9ff","repo":"can1357/oh-my-pi","slug":"expected-object-key","errorCode":null,"errorMessage":"Expected object key","messagePattern":"Expected object key","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/json-parse.ts","lineNumber":351,"sourceCode":"\t\t\t}\n\t\t\tif (this.#partial) return out;\n\t\t\tthrow new SyntaxError(\"Expected ',' or ']' in array\");\n\t\t}\n\t}\n\n\t#key(): string {\n\t\tconst c = this.#s[this.#i];\n\t\tif (c === '\"' || c === \"'\") return this.#string(this.#s.charCodeAt(this.#i));\n\t\t// Unquoted identifier key: read until a structural delimiter / whitespace.\n\t\tconst start = this.#i;\n\t\twhile (this.#i < this.#n) {\n\t\t\tconst ch = this.#s[this.#i];\n\t\t\tif (ch === \":\" || ch === \",\" || ch === \"}\" || isWhitespace(this.#s.charCodeAt(this.#i))) break;\n\t\t\tthis.#i++;\n\t\t}\n\t\tif (this.#i === start) {\n\t\t\tif (this.#partial) return \"\";\n\t\t\tthrow new SyntaxError(\"Expected object key\");\n\t\t}\n\t\treturn this.#s.slice(start, this.#i);\n\t}\n\n\t#string(quote: number): string {\n\t\tconst s = this.#s;\n\t\tconst n = this.#n;\n\t\tlet i = this.#i + 1; // skip opening quote\n\t\tlet out = \"\";\n\t\tlet runStart = i;\n\t\twhile (i < n) {\n\t\t\tconst cc = s.charCodeAt(i);\n\t\t\tif (cc !== BACKSLASH && cc !== quote) {\n\t\t\t\ti++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (cc === quote) {\n\t\t\t\t// Apostrophe / inner-quote recovery (a quote that isn't followed by a","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/json-parse.ts#L333-L369","documentation":"Thrown by RelaxedJson.#key in strict mode when a key position inside an object contains no key characters — the current char is not a quote and not an identifier character. Partial mode returns an empty-string key instead of throwing.","triggerScenarios":"parseJsonWithRepair with a stray delimiter where a key should be, e.g. '{: 1}', '{,}', '{\"a\": 1, :2}', or a punctuation char starting the key slot in strict mode (stray commas alone are already tolerated, so something like ':' or ';' must sit in the key position).","commonSituations":"Malformed LLM output with a stray colon or punctuation in the key slot; template interpolation producing '{: value}' when the key variable is empty; mangled output from an upstream serializer.","solutions":["Fix the input so each key is a quoted string or an identifier","Check whether an interpolated key variable is empty in the code that builds the JSON","Use parseStreamingJson if the input may be an incomplete streaming prefix","Validate tool-call arguments against a schema and skip the malformed call rather than executing it"],"exampleFix":"// before\nconst v = parseJsonWithRepair(`{: 1}`); // SyntaxError: Expected object key\n// after\nconst v = parseJsonWithRepair(`{\"key\": 1}`); // {key: 1}","handlingStrategy":"validation","validationCode":"// Reject payloads where a punctuation char occupies a key position\nconst stripped = text.replace(/\"(\\\\.|[^\"\\\\])*\"/g, '\"S\"');\nconst badKey = /\\{\\s*[:;,.]/.test(stripped) || /,\\s*[:;]/.test(stripped);","typeGuard":null,"tryCatchPattern":"try {\n  const value = parseJsonWithRepair<T>(text);\n} catch (err) {\n  if (err instanceof SyntaxError && err.message === \"Expected object key\") {\n    // reject the call; check the producing template for an empty/missing key variable\n  } else throw err;\n}","preventionTips":["Check interpolated key variables are non-empty before building JSON","Build objects with JSON.stringify from real objects, not string templates","Schema-validate parsed output; discard malformed tool calls instead of executing them"],"tags":["json","parse","object-key","syntaxerror"],"backgroundTag":"json-invalid-object-key","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}