temporalio/temporal · error
membershipExpiry duration should be atleast 1 second
Error message
membershipExpiry duration should be atleast 1 second
What it means
ErrInvalidMembershipExpiry is an exported sentinel in common/persistence/cluster_metadata_store.go, returned by UpsertClusterMembership when the membershipExpiry duration is less than one second. Cluster membership records carry an expiry used for liveness/pruning; durations below one second would cause records to be immediately considered stale.
Source
Thrown at common/persistence/cluster_metadata_store.go:15
package persistence
import (
"context"
"errors"
"go.temporal.io/api/serviceerror"
persistencespb "go.temporal.io/server/api/persistence/v1"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/persistence/serialization"
)
var (
// ErrInvalidMembershipExpiry is used when upserting new cluster membership with an invalid duration
ErrInvalidMembershipExpiry = errors.New("membershipExpiry duration should be atleast 1 second")
// ErrIncompleteMembershipUpsert is used when upserting new cluster membership with missing fields
ErrIncompleteMembershipUpsert = errors.New("membership upserts require all fields")
)
type (
// clusterMetadataManagerImpl implements MetadataManager based on MetadataStore and Serializer
clusterMetadataManagerImpl struct {
serializer serialization.Serializer
persistence ClusterMetadataStore
currentClusterName string
logger log.Logger
}
)
var _ ClusterMetadataManager = (*clusterMetadataManagerImpl)(nil)
// NewClusterMetadataManagerImpl returns new ClusterMetadataManagerView on GitHub (pinned to bde624efd1)
Solutions
- Set membershipExpiry/RecordExpiry to at least 1 * time.Second in the caller
- Fix the dynamic config or static config value feeding the expiry
- Compare with errors.Is(err, persistence.ErrInvalidMembershipExpiry) and apply a sane default when it is returned
Example fix
// before
req.RecordExpiry = cfg.MembershipExpirySeconds // 0 when unset
// after
expiry := cfg.MembershipExpirySeconds
if expiry < time.Second {
expiry = time.Second
}
req.RecordExpiry = expiry Defensive patterns
Strategy: validation
Validate before calling
func validMembershipExpiry(d time.Duration) bool { return d >= time.Second } Type guard
if req.RecordExpiry < time.Second { return persistence.ErrInvalidMembershipExpiry } Try / catch
_, err := manager.UpsertClusterMembership(ctx, req)
if errors.Is(err, persistence.ErrInvalidMembershipExpiry) {
return fmt.Errorf("membership expiry must be >= 1s, got %v", req.RecordExpiry)
} Prevention
- Centralize a default/minimum expiry constant and clamp configured values
- Watch for millisecond-vs-second unit confusion in config keys
- Add a unit test asserting upserts with sub-second expiry are rejected
When it happens
Trigger: Calling UpsertClusterMembership (via the cluster metadata manager/store) with RecordExpiry (membershipExpiry) set to 0 or any duration < 1s, as covered by TestClusterMembershipUpsertInvalidExpiry.
Common situations: Misconfigured membership heartbeat/expiry settings where the value was left at its zero value; unit confusion (configuring milliseconds where seconds are expected); skipping validation on a code path that builds membership upsert requests dynamically.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- membership upserts require all fields
- link type is empty
- link type contains invalid char (valid chars: alphanumeric,
- empty operation name
- empty operation token
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/2e340b4e91b7207c.
Report an issue: GitHub.