gastownhall/beads · error

failed to parse issue from JSONL: %w

Error message

failed to parse issue from JSONL: %w

What it means

parseJSONLFile reads a beads JSONL export line by line. After skipping schema headers and memory records, each remaining line is unmarshaled into types.Issue; if the JSON does not decode into a valid Issue struct (malformed JSON, wrong types for fields, truncated line), the whole parse aborts with this wrapped error.

Source

Thrown at cmd/bd/import_shared.go:1041

		// Check if this is a memory record
		if rawType, ok := peek["_type"]; ok {
			var typeStr string
			if err := json.Unmarshal(rawType, &typeStr); err == nil && typeStr == "memory" {
				var mem memoryRecord
				if err := json.Unmarshal([]byte(line), &mem); err != nil {
					return nil, nil, fmt.Errorf("failed to parse memory record: %w", err)
				}
				if mem.Key != "" && mem.Value != "" {
					configEntries[kvPrefix+memoryPrefix+mem.Key] = mem.Value
				}
				continue
			}
		}

		// Regular issue record
		var issue types.Issue
		if err := json.Unmarshal([]byte(line), &issue); err != nil {
			return nil, nil, fmt.Errorf("failed to parse issue from JSONL: %w", err)
		}
		// Skip tombstone entries: these are deleted issues exported by older
		// versions (pre-v0.50) with status "tombstone" and deleted_at set.
		// They are not valid for re-import since "tombstone" is not a real status.
		if issue.Status == "tombstone" {
			continue
		}

		applyImportWispPlane(peek, &issue)

		issue.SetDefaults()
		issues = append(issues, &issue)
	}
	if err := scanner.Err(); err != nil {
		return nil, nil, fmt.Errorf("failed to scan JSONL: %w", err)
	}

	return issues, configEntries, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Open the file and run each line through `jq -e .` (or `python -m json.tool`) to find the malformed line named in the wrapped %w error
  2. Fix or delete the corrupt line (or re-resolve the git merge conflict), keeping one valid JSON object per line
  3. If the file came from `bd export`, regenerate it with the same bd version instead of hand-editing
  4. Check for version skew: re-export with the current `bd` binary rather than importing an old-format file

Example fix

// before (corrupt line in .beads/issues.jsonl)
{"id":"bd-1","title":"fix" "priority":1
// after (valid single-line JSON object)
{"id":"bd-1","title":"fix","priority":1}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the JSONL before import: one JSON object per line
for i, line := range strings.Split(string(data), "\n") {
    line = strings.TrimSpace(line)
    if line == "" { continue }
    var probe map[string]any
    if err := json.Unmarshal([]byte(line), &probe); err != nil {
        return fmt.Errorf("line %d is not valid JSON: %w", i+1, err)
    }
}

Type guard

func isHeaderRecord(line string) bool {
    var peek map[string]json.RawMessage
    if json.Unmarshal([]byte(line), &peek) != nil { return false }
    _, ok := peek["_schema"]
    return ok
}

Try / catch

if _, _, err := parseJSONLFile(path); err != nil {
    var parseErr *json.UnmarshalTypeError
    if errors.As(err, &parseErr) {
        log.Fatalf("malformed issue record (field %s): %v", parseErr.Field, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling `bd init --from-jsonl`, `bd bootstrap`, or the auto-import path (maybeAutoImportJSONL / importFromLocalJSONLWithOpts) on a JSONL file where a line that is not a _schema header and not a _type:"memory" record fails json.Unmarshal into types.Issue — e.g. hand-edited lines, corrupted merges, or type mismatches (a string where a timestamp is expected).

Common situations: Git merge conflicts left half-resolved in .beads/issues.jsonl; an external script appended non-beads JSON to the export; a partial/truncated file written during a crash; an export from an incompatible bd version with shifted field types.

Understand the failure class

Related errors


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