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 memoryRecord

View on GitHub (pinned to 71377f2769)

Solutions

  1. Open the file at the reported line/offset and fix or remove the malformed JSON line
  2. Re-export cleanly with `bd export -o file.jsonl` instead of hand-editing
  3. If piping, check the upstream command for extra non-JSON output and redirect it to stderr
  4. 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

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

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/4fff2838abcf1af4. Report an issue: GitHub.