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 ClusterMetadataManager

View on GitHub (pinned to bde624efd1)

Solutions

  1. Set membershipExpiry/RecordExpiry to at least 1 * time.Second in the caller
  2. Fix the dynamic config or static config value feeding the expiry
  3. 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

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


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/2e340b4e91b7207c. Report an issue: GitHub.