gitbutlerapp/gitbutler · error

Failed to serialize hunk_header

Error message

Failed to serialize hunk_header: {e}

What it means

When persisting a HunkAssignment back to the database, the optional hunk_header must be JSON-serialized into the row's text column. If serde_json::to_string fails (should be rare for serializable header structs), try_from aborts with this error.

Solutions

  1. Inspect the HunkHeader contents that fail to serialize (non-JSON-serializable fields)
  2. Fix the HunkHeader type so all fields are JSON-serializable (e.g. use String keys in maps)
  3. Fallback: store the header via Display instead of JSON if it is plain text

Example fix

// before
serde_json::to_string(&h).map_err(|e| anyhow!("Failed to serialize hunk_header: {e}"))?
// after
hunk_header.map(|h| h.to_string()) // if plain-text storage is acceptable
Defensive patterns

Strategy: try-catch

Try / catch

match but_db::HunkAssignment::try_from(assignment) {
    Ok(row) => store(row),
    Err(e) if e.to_string().contains("serialize hunk_header") => {
        log::error!("hunk_header not JSON-serializable: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: TryFrom<HunkAssignment> for but_db::HunkAssignment where value.hunk_header is Some(h) and serde_json::to_string(&h) fails — e.g. header contains a map with non-string keys or a type that cannot serialize to JSON.

Common situations: Custom/unusual hunk header payloads introduced by upstream parser changes; type changes in HunkHeader that broke JSON serializability.

Understand the failure class

Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/073cd92dcce2de4f. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-hunk-assignment/src/lib.rs:143

                .branch_ref_bytes
                .map(|b| gix::refs::FullName::try_from(BString::from(b)))
                .transpose()
                .map_err(|e| anyhow::anyhow!("Failed to parse branch_ref_bytes: {e}"))?,
            line_nums_added: None,   // derived data (not persisted)
            line_nums_removed: None, // derived data (not persisted)
            diff: None,              // derived data (not persisted)
        })
    }
}

impl TryFrom<HunkAssignment> for but_db::HunkAssignment {
    type Error = anyhow::Error;
    fn try_from(value: HunkAssignment) -> Result<Self, Self::Error> {
        let header = value
            .hunk_header
            .map(|h| {
                serde_json::to_string(&h)
                    .map_err(|e| anyhow::anyhow!("Failed to serialize hunk_header: {e}"))
            })
            .transpose()?;
        Ok(but_db::HunkAssignment {
            id: value.id.map(|id| id.to_string()),
            hunk_header: header,
            path: value.path,
            path_bytes: value.path_bytes.into(),
            stack_id: None,
            branch_ref_bytes: value.branch_ref_bytes.map(|r| r.into_inner().into()),
        })
    }
}

impl From<HunkAssignment> for but_core::DiffSpec {
    fn from(value: HunkAssignment) -> Self {
        let hunk_headers = if let Some(header) = value.hunk_header {
            vec![but_core::HunkHeader {
                old_start: header.old_start,

View on GitHub (pinned to 58e5313667)