{"record":{"id":"159d2ab8b5ff3c14","repo":"gastownhall/beads","slug":"unterminated-quoted-string","errorCode":null,"errorMessage":"unterminated quoted string","messagePattern":"unterminated quoted string","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/batch.go","lineNumber":343,"sourceCode":"\t\t}\n\t\tif c == '\"' {\n\t\t\tinQuote = true\n\t\t\thasToken = true\n\t\t\tcontinue\n\t\t}\n\t\tif c == ' ' || c == '\\t' {\n\t\t\tif hasToken {\n\t\t\t\ttokens = append(tokens, cur.String())\n\t\t\t\tcur.Reset()\n\t\t\t\thasToken = false\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\thasToken = true\n\t\tcur.WriteByte(c)\n\t}\n\tif inQuote {\n\t\treturn nil, fmt.Errorf(\"unterminated quoted string\")\n\t}\n\tif hasToken {\n\t\ttokens = append(tokens, cur.String())\n\t}\n\treturn tokens, nil\n}\n\n// runBatchOp dispatches a single parsed op against the shared transaction.\n// It intentionally does NOT call any of the non-tx cobra handlers; it talks\n// straight to storage.Transaction so everything joins the same SQL tx.\nfunc runBatchOp(ctx context.Context, tx storage.Transaction, op batchOp) (batchOpResult, error) {\n\tactorName := getActor()\n\tresult := batchOpResult{Line: op.line, Op: op.cmd}\n\tswitch op.cmd {\n\tcase \"close\":\n\t\tif len(op.args) < 1 {\n\t\t\treturn result, fmt.Errorf(\"close requires <id>\")\n\t\t}","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/batch.go#L325-L361","documentation":"`tokenizeBatchLine` splits batch lines on whitespace and supports double-quoted strings. If the line ends while still inside a quoted section (no closing `\"`), tokenization fails with this bare error, which `parseBatchScript` then wraps with the line number.","triggerScenarios":"Any batch line containing an unmatched `\"` — e.g. `update bd-1 \"in progress` — where the closing quote is missing before end of line. Escape sequences \\\" and \\\\ are honored; no other backslash escapes end the quote.","commonSituations":"Shell quoting stripping a trailing quote; line truncation from editors or terminals; programmatic generation of scripts that interpolates descriptions containing quotes without escaping.","solutions":["Close the quoted string with a matching `\"` on the same line.","Escape literal quotes inside the value as \\\" (quotes cannot span lines).","Remove quoting if the value has no spaces or tabs.","Use `bd batch --dry-run` to catch tokenization errors before execution."],"exampleFix":"// before (batch file)\ncreate \"Fix login bug\n// after\ncreate \"Fix login bug\"","handlingStrategy":"validation","validationCode":"# Reject lines with an odd number of unescaped double quotes:\nawk '{ line=$0; gsub(/\\\\./,\"\",line); n=gsub(/\"/,\"\",line); if (n % 2 != 0) print \"line \" NR \": unterminated quote\" }' batch.txt","typeGuard":null,"tryCatchPattern":"tokens, err := tokenizeBatchLine(line)\nif err != nil {\n    if err.Error() == \"unterminated quoted string\" {\n        return fmt.Errorf(\"line %d: %w — quotes cannot span lines; escape as \\\\\"\", lineNo, err)\n    }\n    return nil, err\n}","preventionTips":["Escape literal quotes inside values as \\\"; only \\\" and \\\\ are valid escapes.","Keep quoted values on a single line — no multi-line strings in batch scripts.","Generate quoted values via a helper that escapes `\"` and `\\\\` automatically.","Dry-run scripts before execution to surface tokenization errors early."],"tags":["cli","batch","parsing","quoting"],"backgroundTag":"unterminated-quoted-string","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}