temporalio/temporal · error

config validation error: %w

Error message

config validation error: %w

What it means

Returned by serverOptions.loadAndValidate in temporal/server_options.go:100 when the loaded config fails validation. Wrapping covers two sources: config.Config.Validate() failures (structural/semantic checks on persistence, services, etc.) and the check that every requested service name exists in config.Services. The server refuses to start with an internally inconsistent configuration even if the YAML parsed successfully.

Source

Thrown at temporal/server_options.go:100

}

func (so *serverOptions) loadAndValidate() error {
	for serviceName := range so.serviceNames {
		if !slices.Contains(Services, string(serviceName)) {
			return fmt.Errorf("invalid service %q in service list %v", serviceName, so.serviceNames)
		}
	}

	if so.config == nil {
		err := so.loadConfig()
		if err != nil {
			return fmt.Errorf("unable to load config: %w", err)
		}
	}

	err := so.validateConfig()
	if err != nil {
		return fmt.Errorf("config validation error: %w", err)
	}

	return nil
}

func (so *serverOptions) loadConfig() error {
	if so.configFilePath != "" {
		if so.env != "" || so.configDir != "" || so.zone != "" {
			return errors.New("env, config, zone can not be set if configFilePath is set")
		}
		cfg, err := config.Load(
			config.WithConfigFile(so.configFilePath),
		)
		if err != nil {
			return fmt.Errorf("could not load config file: %w", err)
		}
		so.config = cfg
		return nil

View on GitHub (pinned to bde624efd1)

Solutions

  1. Read the wrapped cause: if it names a service, add that service under the 'services' key of your config (or drop it from the requested service list via ServerOption).
  2. If the cause is from Config.Validate(), fix the specific field it reports (commonly persistence/datastores or global settings).
  3. Compare your config against the upstream config_template.yaml for the same Temporal version to find missing required sections.
  4. Ensure the services map in config has an entry for each of frontend, history, matching, worker that your server options request.

Example fix

// config.yaml before — worker requested by options but missing
services:
  frontend:
    rpc:
      grpcPort: 7233
  history:
    rpc:
      grpcPort: 7234
// after — added the missing service block
services:
  frontend:
    rpc:
      grpcPort: 7233
  history:
    rpc:
      grpcPort: 7234
  worker:
    rpc:
      grpcPort: 7239
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check every requested service exists in the config's services map
for _, svc := range []string{"frontend", "history", "matching", "worker"} {
    if _, ok := cfg.Services[svc]; !ok {
        return fmt.Errorf("service %q missing from config services map", svc)
    }
}

Try / catch

if err := server.Start(); err != nil {
    if strings.Contains(err.Error(), "config validation error") {
        log.Fatalf("invalid config: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: temporal.NewServer(...) after config loads OK but: (a) Config.Validate() rejects the config (e.g. missing persistence setup, invalid DB config), or (b) a service requested via temporal.WithFrontendService/WithHistoryService/WithMatchingService/WithWorkerService/WithService (or DefaultsServiceNames) is absent from the 'services' section of the config file.

Common situations: Trimmed-down config files that omit a service section while the server was asked to run that service; upgrading Temporal and the config no longer satisfying new Validate() rules; hand-edited YAML where a services entry was renamed or deleted.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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