{"record":{"id":"58b42bb0235dfe82","repo":"can1357/oh-my-pi","slug":"unterminated-string-literal-in-src","errorCode":null,"errorMessage":"unterminated string literal in \"${src}\"","messagePattern":"unterminated string literal in \"(.+?)\"","errorType":"exception","errorClass":"OmpTypeError","httpStatus":null,"severity":"error","filePath":"packages/omptype/src/ir.ts","lineNumber":215,"sourceCode":"\t\t\tif (end < 0) throw new OmpTypeError(`unterminated date literal in \"${src}\"`);\n\t\t\tconst source = src.slice(i + 2, end).trim();\n\t\t\tconst value = /^\\d+$/.test(source) ? new Date(Number(source)) : new Date(source);\n\t\t\tif (Number.isNaN(value.valueOf())) throw new OmpTypeError(`invalid date literal in \"${src}\"`);\n\t\t\ttoks.push({ t: \"date\", v: value });\n\t\t\ti = end + 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (c === \"'\" || c === '\"') {\n\t\t\tlet j = i + 1;\n\t\t\tlet value = \"\";\n\t\t\tfor (; j < n && src[j] !== c; j++) {\n\t\t\t\tif (src[j] === \"\\\\\") {\n\t\t\t\t\tj++;\n\t\t\t\t\tif (j >= n) break;\n\t\t\t\t}\n\t\t\t\tvalue += src[j];\n\t\t\t}\n\t\t\tif (j >= n) throw new OmpTypeError(`unterminated string literal in \"${src}\"`);\n\t\t\ttoks.push({ t: \"str\", v: value });\n\t\t\ti = j + 1;\n\t\t\tcontinue;\n\t\t}\n\t\tif (c === \"/\") {\n\t\t\tlet j = i + 1;\n\t\t\tfor (; j < n; j++) {\n\t\t\t\tif (src[j] === \"\\\\\") j++;\n\t\t\t\telse if (src[j] === \"/\") break;\n\t\t\t}\n\t\t\tif (j >= n) throw new OmpTypeError(`unterminated regular expression in \"${src}\"`);\n\t\t\tlet end = j + 1;\n\t\t\twhile (end < n && /[dgimsuvy]/.test(src[end])) end++;\n\t\t\tconst source = src.slice(i + 1, j);\n\t\t\tconst flags = src.slice(j + 1, end);\n\t\t\ttry {\n\t\t\t\ttoks.push({ t: \"regex\", v: new RegExp(source, flags) });\n\t\t\t} catch {","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/omptype/src/ir.ts#L197-L233","documentation":"During tokenization of a type expression, a string literal opened with `'` or `\"` reached the end of input without a closing quote (backslash escapes are skipped, but the terminator never appeared). The tokenizer throws rather than treating the rest of the input as the string.","triggerScenarios":"Expressions like `value == 'abc` or `\"unterminated` where the closing quote is missing; escaped quote at the very end consuming the terminator (e.g. `'abc\\` with the real closing quote swallowed).","commonSituations":"Hand-written constraint strings with a dropped quote; quotes stripped by shell escaping or templating engines; trailing backslash before the closing quote.","solutions":["Add the matching closing quote to the string literal.","Ensure backslash escapes are doubled when the expression passes through a shell or template layer (`\\\\` for a literal backslash).","Avoid a lone trailing backslash immediately before the closing quote."],"exampleFix":"// before\ntype(`name == 'foo`);\n// after\ntype(`name == 'foo'`);","handlingStrategy":"validation","validationCode":"function balancedQuotes(s: string): boolean {\n  let q: string | null = null;\n  for (let i = 0; i < s.length; i++) {\n    if (s[i] === \"\\\\\") { i++; continue; }\n    if (q) { if (s[i] === q) q = null; }\n    else if (s[i] === \"'\" || s[i] === '\"') q = s[i];\n  }\n  return q === null;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const t = new TypeExpression(src);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"unterminated string literal\")) {\n    throw new Error(`Add closing quote: ${err.message}`);\n  }\n  throw err;\n}","preventionTips":["Count quotes before passing expressions built via templating.","Double backslashes when expressions pass through shells/templates.","Avoid lone trailing backslashes in literals."],"tags":["parser","string-literal","syntax-error"],"backgroundTag":"unterminated-literal","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}