temporalio/temporal · error

URI is invalid

Error message

URI is invalid

What it means

validateServerTLS checks mutual exclusivity of inline vs file-based TLS material. ServerTLS allows supplying the private key either as a file path (KeyFile) or as inline PEM data (KeyData), but not both at once, because the loader would be ambiguous about which source to trust. This error is returned when both fields are non-empty.

Source

Thrown at common/archiver/constants.go:27

	ArchiveNonRetryableErrorMsg = "Archive method encountered an non-retryable error."
	// ArchiveTransientErrorMsg is the log message when the Archive() method encounters a transient error
	ArchiveTransientErrorMsg = "Archive method encountered a transient error."
	// ArchiveSkippedInfoMsg is the log messsage when the Archive() method encounter an not found error
	ArchiveSkippedInfoMsg = "Archive method encountered not found error and skipped the archival"

	// ErrReasonInvalidURI is the error reason for invalid URI
	ErrReasonInvalidURI = "URI is invalid"
	// ErrReasonInvalidArchiveRequest is the error reason for invalid archive request
	ErrReasonInvalidArchiveRequest = "archive request is invalid"
	// ErrReasonReadHistory is the error reason for failing to read history
	ErrReasonReadHistory = "failed to read history batches"
	// ErrReasonHistoryMutated is the error reason for mutated history
	ErrReasonHistoryMutated = "history was mutated"
)

var (
	// ErrInvalidURI is the error for invalid URI
	ErrInvalidURI = errors.New("URI is invalid")
	// ErrURISchemeMismatch is the error for mismatch between URI scheme and archiver
	ErrURISchemeMismatch = errors.New("URI scheme does not match the archiver")
	// ErrHistoryMutated is the error for mutated history
	ErrHistoryMutated = errors.New("history was mutated")
	// ErrInvalidGetHistoryRequest is the error for invalid GetHistory request
	ErrInvalidGetHistoryRequest = errors.New("get archived history request is invalid")
	// ErrInvalidQueryVisibilityRequest is the error for invalid Query Visibility request
	ErrInvalidQueryVisibilityRequest = errors.New("query visiblity request is invalid")
	// ErrNextPageTokenCorrupted is the error for corrupted GetHistory token
	ErrNextPageTokenCorrupted = errors.New("next page token is corrupted")
	// ErrHistoryNotExist is the error for non-exist history
	ErrHistoryNotExist = errors.New("requested workflow history does not exist")
)

View on GitHub (pinned to bde624efd1)

Solutions

  1. Remove KeyFile from the ServerTLS config and keep only KeyData (or vice versa).
  2. If config comes from multiple layers, ensure only one layer supplies the key and the other leaves the field empty.
  3. If KeyData is injected by a secret manager, set KeyFile: "" explicitly in the base config.
  4. Add a config lint/startup check that fails fast on both fields being set with a clearer operator-facing message.

Example fix

// before
ServerTLS:
  KeyFile: /etc/temporal/tls.key
  KeyData: |
    -----BEGIN PRIVATE KEY-----
// after
ServerTLS:
  KeyData: |
    -----BEGIN PRIVATE KEY-----
Defensive patterns

Strategy: validation

Validate before calling

func validateKeyExclusivity(cfg *config.ServerTLS) error {
	if cfg.KeyFile != "" && cfg.KeyData != "" {
		return fmt.Errorf("set only one of ServerTLS.KeyFile or ServerTLS.KeyData")
	}
	return nil
}

Prevention

When it happens

Trigger: Calling validateGroupTLS/validateServerTLS with a config.ServerTLS where both KeyFile and KeyData are set to non-empty values.

Common situations: Merging TLS config from multiple sources (e.g. a base YAML plus dynamicconfig overrides) so both a key path and pasted PEM data end up set; copying an example config and filling in both fields; secret-management tooling injecting KeyData while KeyFile is already in the config file.

Related errors


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