{"record":{"id":"603b7675aad79d30","repo":"hyperledger/fabric","slug":"bad-normal-message-s","errorCode":null,"errorMessage":"bad normal message: %s","messagePattern":"bad normal message: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"orderer/consensus/smartbft/chain.go","lineNumber":337,"sourceCode":"\t}\n\n\tc.Logger.Debugf(\"Consensus.SubmitRequest, node id %d\", c.Config.SelfID)\n\tif err = c.consensus.SubmitRequest(reqBytes); err != nil {\n\t\treturn errors.Wrapf(err, \"failed to submit request\")\n\t}\n\treturn nil\n}\n\n// Order accepts a message which has been processed at a given configSeq.\n// If the configSeq advances, it is the responsibility of the consenter\n// to revalidate and potentially discard the message\n// The consenter may return an error, indicating the message was not accepted\nfunc (c *BFTChain) Order(env *cb.Envelope, configSeq uint64) error {\n\tseq := c.support.Sequence()\n\tif configSeq < seq {\n\t\tc.Logger.Warnf(\"Normal message was validated against %d, although current config seq has advanced (%d)\", configSeq, seq)\n\t\tif _, err := c.support.ProcessNormalMsg(env); err != nil {\n\t\t\treturn errors.Errorf(\"bad normal message: %s\", err)\n\t\t}\n\t}\n\n\treturn c.submit(env)\n}\n\n// Configure accepts a message which reconfigures the channel and will\n// trigger an update to the configSeq if committed.  The configuration must have\n// been triggered by a ConfigUpdate message. If the config sequence advances,\n// it is the responsibility of the consenter to recompute the resulting config,\n// discarding the message if the reconfiguration is no longer valid.\n// The consenter may return an error, indicating the message was not accepted\nfunc (c *BFTChain) Configure(config *cb.Envelope, configSeq uint64) error {\n\tif err := c.verifier.ConfigValidator.ValidateConfig(config); err != nil {\n\t\treturn err\n\t}\n\tseq := c.support.Sequence()\n\tif configSeq < seq {","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/chain.go#L319-L355","documentation":"Returned by BFTChain.Order when the normal (transaction) message fails re-validation against the current config. If configSeq has fallen behind the chain's actual config sequence, ProcessNormalMsg is re-run and its error is surfaced as 'bad normal message'. The message is rejected and never enters consensus.","triggerScenarios":"SendTx calls Order(env, configSeq) with a configSeq older than c.support.Sequence() (config has advanced since last delivery), AND the re-processed envelope fails ProcessNormalMsg — e.g. it is malformed, is actually a config tx, or fails mvcc/version verification.","commonSituations":"A config update committed while a client's transaction was in flight, invalidating the seq the tx was validated against; clients resubmitting stale transactions after a channel reconfiguration; sending config-update envelopes through the normal tx path.","solutions":["Have the client fetch the latest config seq (re-deliver/reconnect) and resubmit with an up-to-date configSeq","Fix the client so it does not send config transactions via the normal Order path (use Configure)","Read the inner %s text from ProcessNormalMsg (e.g. invalid tx, mvcc conflict) and correct the transaction accordingly","Retry with a fresh envelope after re-validating against current channel config"],"exampleFix":"// before\nchain.Order(staleEnv, oldConfigSeq) // -> bad normal message: ...\n\n// after\nif err := chain.Order(env, currentSeq); err != nil && strings.Contains(err.Error(), \"bad normal message\") {\n    env = rebuildEnvelope(tx) // re-create tx against current channel config\n    if err := chain.Order(env, support.Sequence()); err != nil { return err }\n}","handlingStrategy":"validation","validationCode":"seq := chain.Sequence() // via support/metadata\nif clientConfigSeq < seq {\n    // re-fetch/re-validate the tx against current config before submitting\n    env = revalidateNormalMsg(env)\n    clientConfigSeq = seq\n}\nreturn chain.Order(env, clientConfigSeq)","typeGuard":null,"tryCatchPattern":"if err := chain.Order(env, configSeq); err != nil {\n    if strings.Contains(err.Error(), \"bad normal message\") {\n        // refresh config seq and resubmit a freshly validated envelope\n        return retryWithFreshConfig(env)\n    }\n    return err\n}","preventionTips":["Track the channel's current config sequence client-side and refresh it on config-update events","Never send config transactions through the normal Order path","Handle 'bad normal message' by rebuilding the transaction against current config, not blind-retrying the same bytes"],"tags":["validation","ordering","config-sequence"],"backgroundTag":"stale-config-sequence","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"}