{"record":{"id":"df3e140c58244f62","repo":"temporalio/temporal","slug":"withtokenprovider-is-set-but-no-remote-cluster-tls","errorCode":null,"errorMessage":"WithTokenProvider is set but no remote-cluster TLS is configured: supply global.tls.remoteClusters in config, or pass a provider via WithTLSConfigProvider","messagePattern":"WithTokenProvider is set but no remote-cluster TLS is configured: supply global\\.tls\\.remoteClusters in config, or pass a provider via WithTLSConfigProvider","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"temporal/fx.go","lineNumber":305,"sourceCode":"\t// check that when static hosts are defined, they are defined for all required hosts\n\tif len(so.hostsByService) > 0 {\n\t\tfor _, service := range DefaultServices {\n\t\t\thosts := so.hostsByService[primitives.ServiceName(service)]\n\t\t\tif len(hosts.All) == 0 {\n\t\t\t\treturn serverOptionsProvider{}, fmt.Errorf(\"%w: %v\", missingServiceInStaticHosts, service)\n\t\t\t}\n\t\t}\n\t}\n\n\tif so.config.Global.Authorization.RemoteClusterAuth.Require && so.tokenProvider == nil {\n\t\treturn serverOptionsProvider{}, errors.New(\"global.authorization.remoteClusterAuth.require is true but no TokenProvider is configured: use WithTokenProvider\")\n\t}\n\t// TokenCredentials require TLS (RFC 9700); without a remote-cluster TLS source the first\n\t// cross-cluster dial would fatal-log, with no clear \"you forgot TLS\" diagnostic.\n\t// Coarse check: any remote-cluster TLS entry passes; per-hostname config is still validated\n\t// lazily on first dial.\n\tif so.tokenProvider != nil && so.tlsConfigProvider == nil && len(so.config.Global.TLS.RemoteClusters) == 0 {\n\t\treturn serverOptionsProvider{}, errors.New(\"WithTokenProvider is set but no remote-cluster TLS is configured: supply global.tls.remoteClusters in config, or pass a provider via WithTLSConfigProvider\")\n\t}\n\n\treturn serverOptionsProvider{\n\t\tServerOptions:              so,\n\t\tStopChan:                   stopChan,\n\t\tStartupSynchronizationMode: so.startupSynchronizationMode,\n\n\t\tConfig:      so.config,\n\t\tPProfConfig: &so.config.Global.PProf,\n\t\tLogConfig:   so.config.Log,\n\n\t\tServiceNames:    so.serviceNames,\n\t\tServiceHosts:    so.hostsByService,\n\t\tNamespaceLogger: so.namespaceLogger,\n\n\t\tServiceResolver:                 so.persistenceServiceResolver,\n\t\tCustomDataStoreFactory:          so.customDataStoreFactory,\n\t\tCustomVisibilityStore:           so.customVisibilityStoreFactory,","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/temporal/fx.go#L287-L323","documentation":"TokenCredentials for remote cluster auth must be sent over TLS (RFC 9700); the server performs a coarse startup check that a token provider is backed by some remote-cluster TLS source. If WithTokenProvider is set but neither a TLS config provider (WithTLSConfigProvider) nor global.tls.remoteClusters entries exist, this error aborts startup to avoid an unclear fatal on first cross-cluster dial.","triggerScenarios":"Constructing server options with WithTokenProvider while Global.TLS.RemoteClusters is empty and WithTLSConfigProvider was not called.","commonSituations":"Enabling token auth for cross-cluster without configuring TLS per remote cluster; supplying TLS via a custom provider but forgetting WithTLSConfigProvider; partial migration from plaintext replication to secured cross-cluster links.","solutions":["Add remote cluster TLS entries under global.tls.remoteClusters in config (serverName, rootCA, etc.)","Or pass a TLS source via WithTLSConfigProvider when building server options","If truly intended, verify per-hostname TLS config is validated lazily on first dial and test a cross-cluster connection after boot","Keep token provider and TLS configuration changes together in the same deploy to avoid mismatched states"],"exampleFix":"// before\nopts := temporal.NewServerOptions(...,\n    temporal.WithTokenProvider(tp))\n// after\nopts := temporal.NewServerOptions(...,\n    temporal.WithTokenProvider(tp),\n    temporal.WithTLSConfigProvider(tlsProvider))\n// or in config:\n// global.tls.remoteClusters:\n//   clusterB: { serverName: clusterB.example.com, rootCAFile: /etc/certs/ca.pem }","handlingStrategy":"validation","validationCode":"func validateTokenTLS(cfg *config.Config, hasTLSProvider bool) error {\n    if cfg.Global.Authorization == nil || !cfg.Global.Authorization.HasTokenProviderEquivalent() { return nil }\n    if hasTLSProvider || len(cfg.Global.TLS.RemoteClusters) > 0 { return nil }\n    return errors.New(\"token provider requires remote-cluster TLS or WithTLSConfigProvider\")\n}","typeGuard":null,"tryCatchPattern":"provider, err := temporal.ServerOptionsProvider(...)\nif err != nil {\n    if strings.Contains(err.Error(), \"no remote-cluster TLS is configured\") {\n        return fmt.Errorf(\"supply global.tls.remoteClusters or WithTLSConfigProvider: %w\", err)\n    }\n    return err\n}","preventionTips":["Treat token providers and remote-cluster TLS as a single configuration unit","Keep at least one remoteClusters entry in every multi-cluster deployment config","Test a cross-cluster dial after any change to auth/TLS settings"],"tags":["temporal-server","tls","remote-cluster","authorization"],"backgroundTag":"tls-required-for-token-auth","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}