semaphoreui/semaphore · error

read jwt signing key option

Error message

read jwt signing key option: %w

What it means

Returned by CheckJWTSigningKey in util/jwt.go when the read-only inspection used by 'vault check' cannot load the jwt_signing_key option from the OptionStore. It is a thin wrapping guard around store.GetOption: the %w carries the underlying database error, and none of the key classification (none/active/rekey-pending/legacy/missing) could be performed because the stored value itself was unreadable.

Solutions

  1. Check the wrapped error for the DB-level cause (connection, permissions, missing table)
  2. Ensure migrations have created the options table before running vault check
  3. Retry the check once the database is reachable
  4. Confirm the DB credentials used by the CLI have read access to the options table
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at util/jwt.go:110 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/5d30f5a4f48e6452. Report an issue: GitHub.

Appendix: source

Thrown at util/jwt.go:110

	encrypted, err := encryptJWTKey(pemBytes)
	if err != nil {
		return nil, err
	}

	if err := store.SetOption(jwtSigningKeyOption, encrypted); err != nil {
		return nil, fmt.Errorf("persist signing key: %w", err)
	}

	return pemBytes, nil
}

// CheckJWTSigningKey classifies the stored JWT signing key for `vault check`:
// "" (none), "active:<id>", "rekey pending:<id>", "legacy (no id)", or
// "MISSING KEY <id>". Read-only.
func CheckJWTSigningKey(store OptionStore) (slot string, err error) {
	stored, err := store.GetOption(jwtSigningKeyOption)
	if err != nil {
		return "", fmt.Errorf("read jwt signing key option: %w", err)
	}
	if stored == "" {
		return "", nil
	}
	return Config.classifyOptionSecret(stored), nil
}

// RekeyJWTSigningKey re-encrypts the stored JWT signing key under the active
// option key (stamping its id). It decrypts via the option keyset, the access
// fallback, and — when supplied — oldKey (the legacy `vault rekey --old-key`
// flow). No-op when no key is stored or the ciphertext is unchanged.
func RekeyJWTSigningKey(store OptionStore, oldKey string) error {
	stored, err := store.GetOption(jwtSigningKeyOption)
	if err != nil {
		return fmt.Errorf("read jwt signing key option: %w", err)
	}
	if stored == "" {
		return nil

View on GitHub (pinned to 1774ccb71a)