semaphoreui/semaphore · error

SEMAPHORE_RUNNER_TOKEN and SEMAPHORE_RUNNER_TOKEN_FILE are…

Error message

SEMAPHORE_RUNNER_TOKEN and SEMAPHORE_RUNNER_TOKEN_FILE are mutually exclusive

What it means

Panics from ConfigInit in util/config.go when both the runner token (SEMAPHORE_RUNNER_TOKEN) and the runner token file (SEMAPHORE_RUNNER_TOKEN_FILE) are set at the same time. It is a startup validation guard: the two are alternative ways to supply the same secret, and allowing both would make it ambiguous which token is authoritative, so the process refuses to start until exactly one is configured.

Solutions

  1. Keep only one of the two settings: remove SEMAPHORE_RUNNER_TOKEN or unset runner_token_file / SEMAPHORE_RUNNER_TOKEN_FILE
  2. If you intended to switch from an inline token to a file, clear the old token value before setting the token file path
  3. Audit deployment manifests and .env files for both variables being injected simultaneously
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at util/config.go:940 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/7fb19dcdc145569c. Report an issue: GitHub.

Appendix: source

Thrown at util/config.go:940

	Cookie = securecookie.New(hash, encryption)

	if Config.WebHost != "" {
		var err error
		WebHostURL, err = url.Parse(Config.WebHost)
		if err != nil {
			panic(err)
		}

		if len(WebHostURL.String()) == 0 {
			WebHostURL = nil
		}
	} else {
		WebHostURL = nil
	}

	if Config.Runner.Token != "" && Config.Runner.TokenFile != "" {
		panic("SEMAPHORE_RUNNER_TOKEN and SEMAPHORE_RUNNER_TOKEN_FILE are mutually exclusive")
	}

	if Config.Runner.TokenFile != "" {
		runnerTokenBytes, err := os.ReadFile(Config.Runner.TokenFile)
		if err == nil {
			Config.Runner.Token = strings.TrimSpace(string(runnerTokenBytes))
		}
	}

	if Config.Subscription.KeyFile != "" {
		subscriptionKeyBytes, err := os.ReadFile(Config.Subscription.KeyFile)
		if err != nil {
			panic(err)
		}

		Config.Subscription.Key = strings.TrimSpace(string(subscriptionKeyBytes))
	}

View on GitHub (pinned to 1774ccb71a)