temporalio/temporal · error

env, config, zone can not be set if configFilePath is set

Error message

env, config, zone can not be set if configFilePath is set

What it means

loadConfig refuses to combine configFilePath with env, configDir ("config"), or zone settings. Loading a single explicit file is mutually exclusive with directory/env/zone-based config resolution, so this guard prevents ambiguous configuration sources and returns this validation error from loadAndValidate.

Source

Thrown at temporal/server_options.go:109

	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
	}
	cfg, err := config.Load(
		config.WithEnv(so.env),
		config.WithConfigDir(so.configDir),
		config.WithZone(so.zone),
	)
	if err != nil {
		return fmt.Errorf("could not load config file: %w", err)
	}

View on GitHub (pinned to bde624efd1)

Solutions

  1. Unset env/configDir/zone (clear TEMPORAL_ENV / TEMPORAL_CONFIG_DIR and --env/--zone flags) when using configFilePath
  2. Or drop configFilePath and rely on env/configDir/zone resolution instead
  3. In code, choose one mode: either config.WithConfigFile(...) or config.WithEnv/WithConfigDir/WithZone
  4. Wrap config loading so conflicting sources are detected and reported with which flags collided

Example fix

// before
opts := temporal.NewServerOptions(
    temporal.WithConfigFilePath("/etc/temporal/config.yaml"),
    temporal.WithEnv("prod"), // conflicts
)
// after
opts := temporal.NewServerOptions(
    temporal.WithConfigFilePath("/etc/temporal/config.yaml"),
)
Defensive patterns

Strategy: validation

Validate before calling

func pickConfigSource(env, zone, configDir, configFilePath string) (string, error) {
    var set []string
    if env != "" { set = append(set, "env") }
    if configDir != "" { set = append(set, "configDir") }
    if zone != "" { set = append(set, "zone") }
    if configFilePath != "" { set = append(set, "configFilePath") }
    if configFilePath != "" && len(set) > 1 {
        return "", fmt.Errorf("configFilePath cannot be combined with: %v", set[:len(set)-1])
    }
    return configFilePath, nil
}

Try / catch

if err := so.loadAndValidate(); err != nil {
    if strings.Contains(err.Error(), "env, config, zone can not be set if configFilePath") {
        return fmt.Errorf("clear env/config-dir/zone flags or drop --config: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling server option loaders (NewServerOptions/NewConfig etc., or the CLI) with both a config file path and env/config-dir/zone set — e.g. ENVIRONMENT=prod TEMPORAL_CONFIG_DIR=... plus --config /path/to/config.yaml, or code setting so.configFilePath and so.env together.

Common situations: Container images with env-based config baked in, then a user mounts/passes an explicit config file; CLI invocations mixing --env flags with --config; scripts exporting TEMPORAL_ENV while also pointing at a file.

Related errors


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