temporalio/temporal · error

cannot specify KeyFile and KeyData at the same time

Error message

cannot specify KeyFile and KeyData at the same time

What it means

Same mutual-exclusion rule as the cert: validateWorkerTLS rejects WorkerTLS configs that specify both KeyFile (key on disk) and KeyData (inline key PEM). The private key must have exactly one source to avoid ambiguity. Checked during root TLS validation before any client config is created.

Source

Thrown at common/rpc/encryption/tls_factory.go:112

	}
	for host, hostConfig := range cfg.PerHostOverrides {

		if strings.TrimSpace(host) == "" {
			return fmt.Errorf("host name cannot be empty string")
		}
		if err := validateServerTLS(&hostConfig); err != nil {
			return err
		}
	}
	return nil
}

func validateWorkerTLS(cfg *config.WorkerTLS) error {
	if cfg.CertFile != "" && cfg.CertData != "" {
		return fmt.Errorf("cannot specify CertFile and CertData at the same time")
	}
	if cfg.KeyFile != "" && cfg.KeyData != "" {
		return fmt.Errorf("cannot specify KeyFile and KeyData at the same time")
	}
	return validateClientTLS(&cfg.Client)
}

func validateServerTLS(cfg *config.ServerTLS) error {
	if cfg.CertFile != "" && cfg.CertData != "" {
		return fmt.Errorf("cannot specify CertFile and CertData at the same time")
	}
	if cfg.KeyFile != "" && cfg.KeyData != "" {
		return fmt.Errorf("cannot specify KeyFile and KeyData at the same time")
	}
	if err := validateCAs(cfg.ClientCAData); err != nil {
		return fmt.Errorf("invalid ServerTLS.ClientCAData: %w", err)
	}
	if err := validateCAs(cfg.ClientCAFiles); err != nil {
		return fmt.Errorf("invalid ServerTLS.ClientCAFiles: %w", err)
	}
	if len(cfg.ClientCAFiles) > 0 && len(cfg.ClientCAData) > 0 {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Keep exactly one of KeyFile or KeyData; remove the other.
  2. Audit merged dynamic config layers for duplicate key settings.
  3. If inline data is preferred for containers, delete the stale file path entry.

Example fix

// before
keyFile: /etc/certs/client.key
keyData: |
  -----BEGIN PRIVATE KEY-----...
// after
keyData: |
  -----BEGIN PRIVATE KEY-----...   # keyFile removed
Defensive patterns

Strategy: validation

Validate before calling

if cfg.KeyFile != "" && cfg.KeyData != "" {
    return errors.New("worker TLS key: set only one of KeyFile/KeyData")
}

Try / catch

if err := validateWorkerTLS(&cfg.Global.WorkerTLS); err != nil {
    return fmt.Errorf("worker TLS config invalid: %w", err)
}

Prevention

When it happens

Trigger: WorkerTLS block where cfg.KeyFile and cfg.KeyData are both non-empty — duplicated key configuration across config layers or a partial migration from file to inline secrets.

Common situations: Secrets tooling injecting keyData while the old keyFile remains; copying an example config that used inline data onto an environment that already uses file paths.

Related errors


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