gastownhall/beads · error

failed to scan JSONL: %w

Error message

failed to scan JSONL: %w

What it means

After parsing all lines of a JSONL file, parseJSONLFile checks bufio.Scanner.Err(). This error fires when the underlying reader itself failed (I/O error reading the file), as opposed to a per-line JSON problem — a distinct failure from the line-parse errors.

Source

Thrown at cmd/bd/import_shared.go:1056

		// 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
}

// applyImportWispPlane resolves which storage plane (wisps vs issues table) a
// parsed import record routes to, shared by every JSONL parse loop
// (parseImportRecords for `bd import` in both storage modes, parseJSONLFile
// for bootstrap / init --from-jsonl / auto-import).
//
// The "wisp_plane" peek key is the EXPLICIT wisps-plane marker (bd-r9uce):
// export writes it for rows that live in the wisps table, precisely because
// row flags cannot be trusted for the plane decision — a promoted no-history
// wisp is a durable issues-table row that may still carry no_history=true
// (PromoteFromEphemeralInTx used to clear only Ephemeral, and wild data
// with that shape persists). Routing such a record by flags re-planes it
// into the wisps table, after which its cross-plane relations are dropped
// by the batch import and the row itself is no longer durable — silent data

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the command — transient I/O errors (network mount blips) often clear on retry
  2. Verify the file exists and is readable: `ls -l .beads/issues.jsonl && head -1 .beads/issues.jsonl`
  3. Check filesystem/mount health (dmesg for I/O errors; remount the network share)
  4. Regenerate the JSONL with `bd export` if the file is damaged, then re-import
Defensive patterns

Strategy: retry

Validate before calling

f, err := os.Open(path)
if err != nil { return err }
st, err := f.Stat()
if err != nil { return err }
if !st.Mode().IsRegular() { return fmt.Errorf("%s is not a regular file", path) }
f.Close()

Try / catch

if _, _, err := parseJSONLFile(path); err != nil {
    if strings.Contains(err.Error(), "failed to scan JSONL") {
        // transient I/O: back off and retry
        time.Sleep(2 * time.Second)
        return parseJSONLFile(path)
    }
    return err
}

Prevention

When it happens

Trigger: During parseJSONLFile (auto-import, bootstrap, init --from-jsonl), the scanner reading the JSONL file hits an I/O error: the file was deleted/truncated mid-read, the filesystem returned EIO, a network mount dropped, or the path is a special file that errors on read.

Common situations: .beads/issues.jsonl lives on an NFS/cloud-synced folder that disconnected mid-import; the file was replaced or truncated while `bd` ran; disk errors or permissions changing mid-read; reading a FIFO/device that errored.

Related errors


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