hyperledger/fabric · error

bad config metadata option CollectTimeout

Error message

bad config metadata option CollectTimeout

What it means

ConfigFromMetadataOptions parses CollectTimeout from the BFT metadata options via time.ParseDuration and wraps parse failures with this error. CollectTimeout bounds how long the leader waits to collect signatures for a batch; an invalid value causes the config block to be rejected.

Source

Thrown at orderer/consensus/smartbft/util/util.go:54

	}
	if config.RequestComplainTimeout, err = time.ParseDuration(options.RequestComplainTimeout); err != nil {
		return config, errors.Wrap(err, "bad config metadata option RequestComplainTimeout")
	}
	if config.RequestAutoRemoveTimeout, err = time.ParseDuration(options.RequestAutoRemoveTimeout); err != nil {
		return config, errors.Wrap(err, "bad config metadata option RequestAutoRemoveTimeout")
	}
	if config.ViewChangeResendInterval, err = time.ParseDuration(options.ViewChangeResendInterval); err != nil {
		return config, errors.Wrap(err, "bad config metadata option ViewChangeResendInterval")
	}
	if config.ViewChangeTimeout, err = time.ParseDuration(options.ViewChangeTimeout); err != nil {
		return config, errors.Wrap(err, "bad config metadata option ViewChangeTimeout")
	}
	if config.LeaderHeartbeatTimeout, err = time.ParseDuration(options.LeaderHeartbeatTimeout); err != nil {
		return config, errors.Wrap(err, "bad config metadata option LeaderHeartbeatTimeout")
	}
	config.LeaderHeartbeatCount = options.LeaderHeartbeatCount
	if config.CollectTimeout, err = time.ParseDuration(options.CollectTimeout); err != nil {
		return config, errors.Wrap(err, "bad config metadata option CollectTimeout")
	}
	config.SyncOnStart = options.SyncOnStart
	config.SpeedUpViewChange = options.SpeedUpViewChange

	if options.LeaderRotation != smartbft.Options_ROTATION_ON {
		config.LeaderRotation = false
		config.DecisionsPerLeader = 0
	} else {
		config.LeaderRotation = true
		config.DecisionsPerLeader = options.DecisionsPerLeader
	}

	if err = config.Validate(); err != nil {
		return config, errors.Wrap(err, "config validation failed")
	}

	if options.RequestMaxBytes == 0 {
		config.RequestMaxBytes = config.RequestBatchMaxBytes

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Fix CollectTimeout in the consensus metadata to a valid duration string like "1s"
  2. Regenerate and recommit the channel config block with correct options
  3. Validate the block with `orderer genesis inspect` before committing
  4. Check tooling output serialization to ensure durations carry Go units

Example fix

// before
"CollectTimeout": "1 second"
// after
"CollectTimeout": "1s"
Defensive patterns

Strategy: validation

Validate before calling

if _, err := time.ParseDuration(opts.CollectTimeout); err != nil { return fmt.Errorf("CollectTimeout invalid: %w", err) }

Try / catch

cfg, err := util.ConfigFromMetadataOptions(selfID, opts)
if err != nil { return fmt.Errorf("rejecting options: %w", err) }

Prevention

When it happens

Trigger: Orderer startup (HandleChain), channel config commit via configBlockToBFTConfig, or `inspect` when options.CollectTimeout is empty or malformed (e.g. '15seconds' instead of '15s').

Common situations: Config generated by external tooling writing unsupported duration formats, hand-edited JSON/YAML in the consensus metadata, or dropped field after version migration of the block metadata.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/ea2d9dd2de513f8d. Report an issue: GitHub.