{"record":{"id":"273a76b9f02eb9d8","repo":"GitoxideLabs/gitoxide","slug":"bug-packed-refs-cannot-contain-symbolic-refs-cat","errorCode":null,"errorMessage":"BUG: packed refs cannot contain symbolic refs, catch that in prepare(…)","messagePattern":"BUG: packed refs cannot contain symbolic refs, catch that in prepare\\(…\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-ref/src/store/packed/transaction.rs","lineNumber":256,"sourceCode":"fn write_edit(out: &mut dyn std::io::Write, edit: &Edit, lines_written: &mut i32) -> std::io::Result<()> {\n    match edit.inner.change {\n        Change::Delete { .. } => {}\n        Change::Update {\n            new: Target::Object(target_oid),\n            ..\n        } => {\n            write!(out, \"{target_oid} \")?;\n            out.write_all(edit.inner.name.as_bstr())?;\n            out.write_all(b\"\\n\")?;\n            if let Some(object) = edit.peeled {\n                writeln!(out, \"^{object}\")?;\n            }\n            *lines_written += 1;\n        }\n        Change::Update {\n            new: Target::Symbolic(_),\n            ..\n        } => unreachable!(\"BUG: packed refs cannot contain symbolic refs, catch that in prepare(…)\"),\n    }\n    Ok(())\n}\n\n/// Convert this buffer to be used as the basis for a transaction.\npub(crate) fn buffer_into_transaction(\n    buffer: file::packed::SharedBufferSnapshot,\n    lock_mode: gix_lock::acquire::Fail,\n    precompose_unicode: bool,\n    namespace: Option<Namespace>,\n) -> Result<packed::Transaction, gix_lock::acquire::Error> {\n    let lock = gix_lock::File::acquire_to_update_resource(&buffer.path, lock_mode, None)?;\n    Ok(packed::Transaction {\n        buffer: Some(buffer),\n        lock: Some(lock),\n        closed_lock: None,\n        edits: None,\n        precompose_unicode,","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-ref/src/store/packed/transaction.rs#L238-L274","documentation":"An `unreachable!()` guard in the packed-refs edit writer: packed-refs files cannot represent symbolic references, so when the transaction applies an `Change::Update` whose new target is `Target::Symbolic` against the packed buffer, the code panics with a message directing you to `prepare(…)`. The transaction preparation step is responsible for rejecting or splitting such updates before they reach this code path.","triggerScenarios":"Calling `Transaction::commit` (which invokes `write_edit`) with a prepared transaction that still contains an `Update` change targeting a symbolic ref destined for the packed-refs store. This indicates `prepare(…)` failed to catch it — a gitoxide bug, not a user error.","commonSituations":"Only reachable by users through a bug in gix-ref's transaction preparation, or when writing custom transaction plumbing that bypasses `prepare`.","solutions":["Ensure all ref updates go through `gix_ref::transaction::Transaction` and its `prepare(…)` step, which filters symbolic updates away from packed storage.","If hit, file a bug with the reproducing transaction changes; symbolic targets must be written as loose refs instead.","As a workaround, split the update: delete/log the packed ref and write the symbolic ref as a loose ref."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before committing a transaction, ensure no symbolic-ref updates target packed storage:\nlet has_symbolic_update = changes.iter().any(|c| matches!(c, gix_ref::transaction::Change::Update { new: gix_ref::Target::Symbolic(_), .. }));\nif has_symbolic_update { /* route that ref to loose storage or handle before prepare/commit */ }","typeGuard":null,"tryCatchPattern":"match transaction.commit(commit_resource) {\n    Ok(edit) => { /* apply */ }\n    Err(err) if err.to_string().contains(\"packed refs cannot contain symbolic refs\") => {\n        // library bug: split the symbolic update into a loose-ref write and retry\n    }\n    Err(err) => return Err(err.into()),\n}","preventionTips":["Always go through `Transaction::prepare(…)` before `commit`; never hand-roll edit application.","Write symbolic refs as loose refs, never into packed-refs storage.","Keep gix-ref updated; if this panic fires, it is a prepare() bug — report it with a reproducer."],"tags":["rust","panic","unreachable","gix-ref","symbolic-ref"],"backgroundTag":"invalid-state-transition","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}