temporalio/temporal · info
watch not supported
Error message
watch not supported
What it means
ErrWatchNotSupported (common/persistence/metadata_manager.go:18) is returned by metadata managers whose backing store cannot push namespace change notifications. The namespace registry (common/namespace/nsregistry) implements a documented fallback: if the persistence layer returns ErrWatchNotSupported when starting a watch (WatchNamespaces at metadata_manager.go:266), it degrades to polling-based refresh instead of failing startup. Callers should treat it as an expected capability signal, not a fault.
Source
Thrown at common/persistence/metadata_manager.go:18
package persistence
import (
"context"
"errors"
enumspb "go.temporal.io/api/enums/v1"
namespacepb "go.temporal.io/api/namespace/v1"
"go.temporal.io/api/serviceerror"
persistencespb "go.temporal.io/server/api/persistence/v1"
"go.temporal.io/server/common"
"go.temporal.io/server/common/log"
"go.temporal.io/server/common/persistence/serialization"
"go.temporal.io/server/common/primitives"
"google.golang.org/protobuf/types/known/durationpb"
)
var ErrWatchNotSupported = errors.New("watch not supported")
type (
// metadataManagerImpl implements MetadataManager based on MetadataStore and Serializer
metadataManagerImpl struct {
serializer serialization.Serializer
persistence MetadataStore
logger log.Logger
clusterName string
}
)
var _ MetadataManager = (*metadataManagerImpl)(nil)
// NewMetadataManagerImpl returns new MetadataManager
func NewMetadataManagerImpl(
persistence MetadataStore,
serializer serialization.Serializer,View on GitHub (pinned to bde624efd1)
Solutions
- Handle it explicitly: if errors.Is(err, persistence.ErrWatchNotSupported) fall back to polling-based namespace refresh (as nsregistry does)
- Verify which metadata store backend your deployment uses and whether it supports watches
- If watch behavior is required, switch to a store implementation that supports it
Example fix
// before
err := mgr.WatchNamespaces(ctx, handler) // fails on SQL stores
// after
if err := mgr.WatchNamespaces(ctx, handler); err != nil {
if errors.Is(err, persistence.ErrWatchNotSupported) {
startPollingRefresher() // degrade gracefully
return nil
}
return err
} Defensive patterns
Strategy: fallback
Type guard
func isWatchNotSupported(err error) bool {
return errors.Is(err, persistence.ErrWatchNotSupported)
} Try / catch
err := mgr.WatchNamespaces(ctx, handler)
if err != nil {
if errors.Is(err, persistence.ErrWatchNotSupported) {
startPollingRefresher(pollInterval) // supported degraded mode
return nil
}
return err
} Prevention
- Model watch support as a capability flag on the store implementation and check before calling WatchNamespaces
- Mirror nsregistry's pattern: errors.Is(ErrWatchNotSupported) -> polling refresher
- Never treat this error as fatal in startup code for SQL-backed deployments
- Cover the fallback path in tests (see TestWatchFallbackToPolling)
When it happens
Trigger: Calling MetadataManager.WatchNamespaces (or namespace registry Start, which calls startWatch) against a store implementation without watch support, e.g. SQL-based metadata stores lacking a change-notification mechanism.
Common situations: Running with SQL persistence (MySQL/PostgreSQL) where no watch mechanism exists; swapping the metadata store backend in tests/SetupTest; registry tests TestWatchFallbackToPolling explicitly exercising the fallback.
Related errors
- corrupted history event batch, wrong version and IDs
- corrupted history event batch, empty events
- page size to read history tasks must be positive
- history task from queue has nil blob
- enqueue task request task is nil
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/1a392d4347195694.
Report an issue: GitHub.