temporalio/temporal · error
ErrNonPositiveTotalNumShards
ErrNonPositiveTotalNumShards
Error message
%w: %d
What it means
NewOwnershipBasedQuotaScaler validates that totalNumShards is positive before constructing the scaler. The returned error wraps ErrNonPositiveTotalNumShards with the offending value, guarding against division/mapping errors in rate-limit scaling math driven by the total shard count.
Source
Thrown at service/history/shard/ownership_based_quota_scaler.go:66
var (
// shardCountNotSet is a sentinel value for the shardCount which indicates that it hasn't been set yet. It's an
// int64 because that's the type of the atomic.
shardCountNotSet int64 = -1
ErrNonPositiveTotalNumShards = errors.New("totalNumShards must be greater than 0")
)
// NewOwnershipBasedQuotaScaler returns an OwnershipBasedQuotaScaler. The updateAppliedCallback field is a channel which
// is sent to in a blocking fashion when the shard count updates are applied. This is useful for testing. In production,
// you should pass in nil, which will cause the callback to be ignored. If totalNumShards is non-positive, then an error
// is returned.
func NewOwnershipBasedQuotaScaler(
shardCounter ShardCounter,
totalNumShards int,
updateAppliedCallback chan struct{},
) (*OwnershipBasedQuotaScalerImpl, error) {
if totalNumShards <= 0 {
return nil, fmt.Errorf("%w: %d", ErrNonPositiveTotalNumShards, totalNumShards)
}
scaler := &OwnershipBasedQuotaScalerImpl{
shardCounter: shardCounter,
totalNumShards: totalNumShards,
updateAppliedCallback: updateAppliedCallback,
subscription: shardCounter.SubscribeShardCount(),
}
scaler.shardCount.Store(shardCountNotSet)
scaler.shutdownWG.Go(func() {
for count := range scaler.subscription.ShardCount() {
scaler.shardCount.Store(int64(count))
if scaler.updateAppliedCallback != nil {
scaler.updateAppliedCallback <- struct{}{}
}
}View on GitHub (pinned to bde624efd1)
Solutions
- Pass the cluster's actual numHistoryShards (positive) from config when constructing the scaler
- Fix the config/dynamicconfig entry that supplies 0 or a negative shard count
- Ensure the ShardCounter is initialized before scaler construction so the total is populated
Example fix
// before
scaler, err := NewOwnershipBasedQuotaScaler(counter, cfg.NumShards, cb)
// after
if cfg.NumShards <= 0 {
return fmt.Errorf("invalid numHistoryShards: %d", cfg.NumShards)
}
scaler, err := NewOwnershipBasedQuotaScaler(counter, cfg.NumShards, cb) Defensive patterns
Strategy: validation
Validate before calling
if totalNumShards <= 0 {
return fmt.Errorf("totalNumShards must be positive, got %d", totalNumShards)
} Try / catch
scaler, err := NewOwnershipBasedQuotaScaler(counter, n, cb)
if errors.Is(err, ErrNonPositiveTotalNumShards) {
// fix configuration before proceeding; do not start with invalid scaling
} Prevention
- Validate numHistoryShards config at startup
- Fail fast if shard counter reports a non-positive total
- Add a unit test constructing the scaler with every configured value source
When it happens
Trigger: Calling NewOwnershipBasedQuotaScaler with totalNumShards <= 0 — e.g. from an uninitialized shard count source, a config value of 0, or a shardCounter whose total was not yet populated.
Common situations: Dynamic config where numHistoryShards is unset/0; a service started before the shard counter was initialized; tests constructing the scaler with a zero default; misconfigured cluster sizing.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- %w: unknown plugin %q, supported plugins: %v
- unable to resolve AWS region for obtaining AWS Elastic signi
- unknown workflow state sync result: %d
- current cluster %q is missing from cluster metadata
- %w: zombie workflow cannot be updated
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/216428d6fcae4534.
Report an issue: GitHub.