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

  1. Handle it explicitly: if errors.Is(err, persistence.ErrWatchNotSupported) fall back to polling-based namespace refresh (as nsregistry does)
  2. Verify which metadata store backend your deployment uses and whether it supports watches
  3. 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

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


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