gastownhall/beads · error
failed to parse JSONL line: %w
Error message
failed to parse JSONL line: %w
What it means
A line of the JSONL stream fed to `bd import` is not valid JSON, so the pre-parse peek (json.Unmarshal into a generic map) fails before record-type dispatch. The wrapped underlying encoding/json error names the exact syntax problem and byte offset.
Source
Thrown at cmd/bd/import.go:255
// reader): the optional _schema header and tombstones are skipped, and the
// "wisp_plane" boolean is honored as the explicit wisps-plane marker (and
// the legacy "wisp" alias for "ephemeral") via applyImportWispPlane.
func parseImportRecords(r io.Reader) ([]*types.Issue, []memoryRecord, error) {
scanner := bufio.NewScanner(r)
scanner.Buffer(make([]byte, 0, 1024*1024), 64*1024*1024)
var issues []*types.Issue
var memories []memoryRecord
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
var peek map[string]json.RawMessage
if err := json.Unmarshal([]byte(line), &peek); err != nil {
return nil, nil, fmt.Errorf("failed to parse JSONL line: %w", err)
}
// Skip the optional beads-jsonl header record (§J1.3). A canonical
// export may prepend a provenance line, e.g.
// {"_schema":"beads-jsonl/1","_dolt_branch":"main","_sort":"stable-v1"}.
// It carries no _type and no issue fields; without this guard it falls
// through to the issue path, unmarshals into an empty Issue, and aborts
// the whole import with "title is required". parseJSONLFile (the
// bootstrap reader) has always skipped it; this loop — the one `bd
// import` and `bd import -` run through — did not.
if _, isHeader := peek["_schema"]; isHeader {
continue
}
if rawType, ok := peek["_type"]; ok {
var typeStr string
if err := json.Unmarshal(rawType, &typeStr); err == nil && typeStr == "memory" {
var mem memoryRecordView on GitHub (pinned to 71377f2769)
Solutions
- Open the file at the reported line/offset and fix or remove the malformed JSON line
- Re-export cleanly with `bd export -o file.jsonl` instead of hand-editing
- If piping, check the upstream command for extra non-JSON output and redirect it to stderr
- Validate the stream first: `while read -r l; do echo "$l" | jq empty || echo "bad: $l"; done < file.jsonl`
Example fix
// before (truncated line)
{"id":"bd-1","title":"Fix
// after
{"id":"bd-1","title":"Fix login bug","status":"open"} Defensive patterns
Strategy: validation
Validate before calling
jq -c empty file.jsonl || echo 'invalid JSONL' # or:
awk '{print NR": "$0}' file.jsonl | while IFS= read -r l; do echo "$l" | jq empty >/dev/null || echo bad; done Try / catch
if ! bd import data.jsonl 2>err.log; then grep 'failed to parse JSONL line' err.log # locate offending line/offset fi
Prevention
- Never hand-edit exported .jsonl; re-export with bd export instead
- Validate JSONL with jq before every import
- Strip tool banners when piping: producer output must be pure JSONL on stdout
- Use LF line endings and remove BOM from generated files
When it happens
Trigger: `bd import file.jsonl` or `bd import -` where a line contains truncated JSON (e.g. a cut-off pipe transfer), stray prose/comment text, concatenated objects on one line, or invalid characters like a lone BOM.
Common situations: Editing an exported .jsonl by hand and breaking a line; piping output of another tool that adds banners; partial file download; Windows CRLF edge cases or copy-paste artifacts.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- parse gh output: %w
- line %d: %w
- line %d: 'dep' requires a subcommand (add|remove)
- line %d: unknown dep subcommand %q (want add|remove)
- line %d: unsupported batch command %q (supported: close, upd
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4fff2838abcf1af4.
Report an issue: GitHub.