temporalio/temporal · error

cannot specify CertFile and CertData at the same time

Error message

cannot specify CertFile and CertData at the same time

What it means

validateWorkerTLS enforces that WorkerTLS config specifies a certificate either by file path (CertFile) or inline data (CertData), never both. Supplying both is ambiguous, so validation fails with this error before any TLS config is built. It is checked when validateRootTLS validates worker TLS settings.

Source

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

	}
	if err := validateClientTLS(&cfg.Client); err != nil {
		return err
	}
	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 {

View on GitHub (pinned to bde624efd1)

Solutions

  1. Remove one of the two: keep CertFile for on-disk certs or CertData for inline PEM, not both.
  2. If both come from merged dynamic config layers, delete the obsolete field from the higher-precedence layer.
  3. After fixing, ensure KeyFile/KeyData follow the same single-source rule.

Example fix

// before
certFile: /etc/certs/client.pem
certData: |
  -----BEGIN CERTIFICATE-----...
// after
certFile: /etc/certs/client.pem   # certData removed
Defensive patterns

Strategy: validation

Validate before calling

if cfg.CertFile != "" && cfg.CertData != "" {
    return errors.New("worker TLS cert: set only one of CertFile/CertData")
}

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 both cfg.CertFile and cfg.CertData are non-empty — e.g. migrating from file-based to inline secrets while keeping the old path, or merging dynamic config layers that each set a different field.

Common situations: Setting certDataFile contents via secret injection while a legacy certFile line remains in the config; stacking multiple dynamic config YAMLs where one sets CertFile and another CertData.

Related errors


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