gastownhall/beads · error

encoding JSON: %v

Error message

encoding JSON: %v

What it means

outputJSONWithPagination encodes the command result (optionally schema-version-wrapped) to stdout with a JSON encoder. If json.Encoder.Encode fails — practically only when the value contains unencodable data (channels, funcs, cycles, NaN/Inf in raw structs) or stdout write fails (broken pipe/ENOSPC) — it returns 'encoding JSON: %v'.

Source

Thrown at cmd/bd/output.go:50

// metadata. When BD_JSON_ENVELOPE=1 and p is non-nil, the envelope gains a
// "pagination" key so programmatic consumers can detect truncation without
// parsing stderr. When the envelope is not active, p is ignored and the
// existing stderr text hint handles the human/text path.
func outputJSONWithPagination(v interface{}, p *PaginationMeta) error {
	var out interface{}
	if jsonEnvelopeEnabled() && p != nil {
		out = map[string]interface{}{
			"schema_version": JSONSchemaVersion,
			"data":           v,
			"pagination":     p,
		}
	} else {
		out = wrapWithSchemaVersion(v)
	}
	encoder := json.NewEncoder(os.Stdout)
	encoder.SetIndent("", "  ")
	if err := encoder.Encode(out); err != nil {
		return fmt.Errorf("encoding JSON: %v", err)
	}

	if !jsonEnvelopeEnabled() {
		emitEnvelopeDeprecation()
	}
	return nil
}

func outputJSONRaw(v interface{}) error {
	encoder := json.NewEncoder(os.Stdout)
	encoder.SetIndent("", "  ")
	if err := encoder.Encode(v); err != nil {
		return fmt.Errorf("encoding JSON: %v", err)
	}
	return nil
}

func wrapWithSchemaVersion(v interface{}) interface{} {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Avoid truncating consumers: use `head -c` alternatives or ignore SIGPIPE, or check that downstream commands keep stdout open.
  2. Check the %v cause: EPIPE means the consumer closed the pipe; ENOSPC means free disk space.
  3. If it's an unencodable value bug, report/fix the command so its output struct is JSON-safe.
  4. Redirect output to a file instead of a short-lived pipe.

Example fix

// before
bd ready --json | head -5       # head exits, Encode gets EPIPE
// after
bd ready --json > ready.json && head -5 ready.json
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure only JSON-encodable values reach outputJSONWithPagination
// (no channels, funcs, cycles; sanitize NaN/Inf floats beforehand)

Type guard

func jsonSafe(v interface{}) bool {
    _, err := json.Marshal(v)
    return err == nil
}

Try / catch

if err := outputJSONWithPagination(v, page); err != nil {
    if errors.Is(err, syscall.EPIPE) || strings.Contains(err.Error(), "broken pipe") {
        return nil // consumer closed stdout; not a real failure
    }
    return fmt.Errorf("emitting JSON output: %w", err)
}

Prevention

When it happens

Trigger: encoder.Encode(out) errors inside outputJSONWithPagination (called by outputJSON, runReadyProxiedList, anonymous callers) — typically stdout closed/EPIPE (e.g. `bd ready --json | head`) or a result type containing unsupported values.

Common situations: Piping bd JSON output into `head` or a command that exits early and closes the pipe; disk full; a command embedding a non-serializable value in its result struct.

Related errors


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