{"record":{"id":"8ac4418ebdcb1f23","repo":"hyperledger/fabric","slug":"error-getting-txid-from-header-payload-header-is","errorCode":null,"errorMessage":"error getting txID from header: payload header is nil","messagePattern":"error getting txID from header: payload header is nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"protoutil/txutils.go","lineNumber":523,"sourceCode":"\treturn hash2.Sum(nil), nil\n}\n\n// GetOrComputeTxIDFromEnvelope gets the txID present in a given transaction\n// envelope. If the txID is empty, it constructs the txID from nonce and\n// creator fields in the envelope.\nfunc GetOrComputeTxIDFromEnvelope(txEnvelopBytes []byte) (string, error) {\n\ttxEnvelope, err := UnmarshalEnvelope(txEnvelopBytes)\n\tif err != nil {\n\t\treturn \"\", errors.WithMessage(err, \"error getting txID from envelope\")\n\t}\n\n\ttxPayload, err := UnmarshalPayload(txEnvelope.Payload)\n\tif err != nil {\n\t\treturn \"\", errors.WithMessage(err, \"error getting txID from payload\")\n\t}\n\n\tif txPayload.Header == nil {\n\t\treturn \"\", errors.New(\"error getting txID from header: payload header is nil\")\n\t}\n\n\tchdr, err := UnmarshalChannelHeader(txPayload.Header.ChannelHeader)\n\tif err != nil {\n\t\treturn \"\", errors.WithMessage(err, \"error getting txID from channel header\")\n\t}\n\n\tif chdr.TxId != \"\" {\n\t\treturn chdr.TxId, nil\n\t}\n\n\tsighdr, err := UnmarshalSignatureHeader(txPayload.Header.SignatureHeader)\n\tif err != nil {\n\t\treturn \"\", errors.WithMessage(err, \"error getting nonce and creator for computing txID\")\n\t}\n\n\ttxid := ComputeTxID(sighdr.Nonce, sighdr.Creator)\n\treturn txid, nil","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/protoutil/txutils.go#L505-L541","documentation":"GetOrComputeTxIDFromEnvelope extracts the transaction ID from an envelope by unmarshalling its Payload and reading the ChannelHeader's TxId. When the envelope's payload unmarshals but its Header field is nil, there is no place the txID could be stored, so it returns this specific wrapped error. This typically means the envelope bytes do not represent a normal transaction envelope.","triggerScenarios":"Calling GetOrComputeTxIDFromEnvelope on an envelope whose Payload decodes to a peer.Payload with Header == nil; passing a config-style or non-transaction envelope (e.g. a bare ChaincodeAction or config envelope) to this function; bytes from a malformed/fabricated client submission.","commonSituations":"Block/ledger store iteration hitting a non-tx record (config transaction stored differently); fabric-sdk clients submitting envelopes missing the payload header; corrupted block files where payload header bytes were zeroed; tests TestBlockfileMgrGetTxById feeding synthetic envelopes.","solutions":["Verify the envelope actually wraps a transaction: unmarshal Envelope → Payload → check payload.Header yourself and handle nil explicitly before calling","Filter out non-transaction records (config envelopes) before extracting txIDs — use block metadata filtered tx validation flags","Re-serialize the envelope correctly on the submission side so payload.Header.ChannelHeader (containing TxId) is set: use protoutil.CreateTxEnvelope or BuildSignedEnvelope","If reading from a corrupted store, skip/repair the record rather than treating it as a transaction"],"exampleFix":"// before\ntxid, err := protoutil.GetOrComputeTxIDFromEnvelope(rawBytes) // panics/errors on config envelope\n// after\nenv, err := protoutil.UnmarshalEnvelope(rawBytes)\nif err != nil { return \"\", err }\npayload, err := protoutil.UnmarshalPayload(env.Payload)\nif err != nil || payload.Header == nil { return \"\", errors.New(\"not a transaction envelope\") }\ntxid, err := protoutil.GetOrComputeTxIDFromEnvelope(rawBytes)","handlingStrategy":"validation","validationCode":"env, err := protoutil.UnmarshalEnvelope(rawBytes)\nif err != nil { return \"\", err }\npayload, err := protoutil.UnmarshalPayload(env.Payload)\nif err != nil { return \"\", err }\nif payload.Header == nil {\n    return \"\", errors.New(\"envelope payload has no header; not a transaction envelope\")\n}\n_, err = protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)\nif err != nil { return \"\", err }\ntxid, err := protoutil.GetOrComputeTxIDFromEnvelope(rawBytes)\n","typeGuard":"func isTxEnvelope(rawBytes []byte) bool {\n    env, err := protoutil.UnmarshalEnvelope(rawBytes)\n    if err != nil { return false }\n    payload, err := protoutil.UnmarshalPayload(env.Payload)\n    if err != nil || payload == nil { return false }\n    return payload.Header != nil && len(payload.Header.ChannelHeader) > 0\n}\n","tryCatchPattern":"txid, err := protoutil.GetOrComputeTxIDFromEnvelope(rawBytes)\nif err != nil {\n    if strings.Contains(err.Error(), \"payload header is nil\") {\n        return \"\", fmt.Errorf(\"skipping non-transaction record: %w\", err)\n    }\n    return \"\", err\n}\n","preventionTips":["Check block metadata (filtered/validation flags) to skip config and non-tx records before txID extraction","On submission side use protoutil.CreateTxEnvelope/BuildSignedEnvelope so payload.Header is always populated","Never assume every stored envelope is an endorser transaction; guard payload.Header reads yourself","Handle each sub-unmarshal (Envelope, Payload, ChannelHeader) explicitly so the failure point is clear"],"tags":["hyperledger-fabric","envelope","txid","malformed-input"],"backgroundTag":"malformed-envelope","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}