gastownhall/beads · warning

encoding string

Error message

encoding string: %w

What it means

writeCanonicalMetadataString emits one JSON string (key or value) using the standard library's escaping via json.Marshal so both sides of a comparison escape identically. json.Marshal on a Go string cannot fail in current Go, so this error is defensive: it only fires if the standard library's marshaler returns an unexpected error, and it wraps it so the cause is preserved.

Solutions

  1. Unwrap the error to read json.Marshal's underlying cause (check Go release notes for encoding/json behavior changes).
  2. Upgrade or pin the Go toolchain to a version where json.Marshal(string) is total.
  3. As a workaround, pre-encode the string yourself with json.Marshal and compare the encoded bytes.
  4. File an issue with the input string if a reproducible marshal failure exists.
Defensive patterns

Strategy: try-catch

Try / catch

if err := storage.CanonicalMetadataValue(raw); err != nil {
    if strings.Contains(err.Error(), "encoding string") {
        // standard-library marshal failure: report toolchain/runtime details
        return fmt.Errorf("canonicalizer string encode failed (go %s): %w", runtime.Version(), err)
    }
    return err
}

Prevention

When it happens

Trigger: Effectively unreachable for plain string inputs — reached only through writeCanonicalMetadataJSON during CanonicalMetadataValue if json.Marshal(string) ever returned an error (e.g. a future Go change or an unsupported rune scenario in the runtime).

Common situations: Developers effectively never hit this in production; if it appears, it signals a Go runtime/standard-library anomaly or code drift in the canonicalizer (the sibling 'cannot canonicalize %T' branch guards similar drift).

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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

Appendix: source

Thrown at internal/storage/metadata_cas.go:221

			if err := writeCanonicalMetadataJSON(buf, typed[key]); err != nil {
				return err
			}
		}
		buf.WriteByte('}')
	default:
		// encoding/json produces only the cases above for an `any` target with
		// UseNumber set, so reaching this means the decoder changed under us.
		return fmt.Errorf("cannot canonicalize %T", value)
	}
	return nil
}

// writeCanonicalMetadataString emits a JSON string with the standard library's
// escaping, which both sides of a comparison go through identically.
func writeCanonicalMetadataString(buf *bytes.Buffer, s string) error {
	encoded, err := json.Marshal(s)
	if err != nil {
		return fmt.Errorf("encoding string: %w", err)
	}
	buf.Write(encoded)
	return nil
}

// truncateMetadataValue bounds a malformed value quoted back at a caller, so a
// megabyte of junk does not become a megabyte of error message.
func truncateMetadataValue(raw json.RawMessage) string {
	const limit = 64
	if len(raw) <= limit {
		return string(raw)
	}
	return string(raw[:limit]) + "…"
}

View on GitHub (pinned to 71377f2769)