semaphoreui/semaphore · error

jwt: unsupported curve

Error message

jwt: unsupported curve %q, expected P-256

What it means

Thrown by parsePrivateKey in pkg/jwt/signer.go when a PKCS#8 PEM key parses successfully as ECDSA but its elliptic curve is not P-256 (e.g. P-384, P-521, or a custom curve). The JWT signer deliberately requires ES256, which is only defined over P-256, so any other curve is rejected before the key is used for signing.

Solutions

  1. Regenerate the signing key with an ECDSA P-256 curve (e.g. openssl ecparam -name prime256v1 -genkey -noout) and re-import the PEM
  2. If the key was provisioned via config or the jwt_signing_key database option, replace that stored key with a P-256 PEM
  3. Verify with 'openssl ec -in key.pem -text -noout' that the curve reports prime256v1 before deploying
  4. If you cannot replace the key, disable the JWT feature until a compliant key is available, since the signer will not start
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pkg/jwt/signer.go:163 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/54656e8e6a8113d2. Report an issue: GitHub.

Appendix: source

Thrown at pkg/jwt/signer.go:163

	set := jose.JSONWebKeySet{Keys: []jose.JSONWebKey{jwk}}
	return json.Marshal(set)
}

func parsePrivateKey(data []byte) (*ecdsa.PrivateKey, error) {
	block, _ := pem.Decode(data)
	if block == nil {
		return nil, errors.New("jwt: no PEM block found in key file")
	}
	key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
	if err != nil {
		return nil, err
	}
	ecKey, ok := key.(*ecdsa.PrivateKey)
	if !ok {
		return nil, fmt.Errorf("jwt: key is not ECDSA (got %T)", key)
	}
	if ecKey.Curve != elliptic.P256() {
		return nil, fmt.Errorf("jwt: unsupported curve %q, expected P-256", ecKey.Curve.Params().Name)
	}
	return ecKey, nil
}

func computeKID(pub *ecdsa.PublicKey) (string, error) {
	der, err := x509.MarshalPKIXPublicKey(pub)
	if err != nil {
		return "", err
	}
	sum := sha256.Sum256(der)
	return base64.RawURLEncoding.EncodeToString(sum[:]), nil
}

func randomJTI() (string, error) {
	var b [16]byte
	if _, err := rand.Read(b[:]); err != nil {
		return "", err
	}

View on GitHub (pinned to 1774ccb71a)