crowdsecurity/crowdsec · error · ErrTokenParse

%w: %s

Error message

%w: %s

What it means

LoadAPICToken calls ParseUnverified to decode the stored CAPI JWT. If the token string cannot be parsed as a JWT (malformed, truncated, not a token at all), the returned error wraps ErrTokenParse with the parser message. This is a data problem in the stored token, not a network or DB connectivity problem.

Source

Thrown at pkg/database/token.go:43

}

// LoadAPICToken attempts to retrieve and validate a JWT token from the local database.
// Errors are returned if the token can't be read, is not valid, expired or has no expiration.
func (c *Client) LoadAPICToken(ctx context.Context, logger logrus.FieldLogger) (APICToken, error) {
	token, err := c.GetConfigItem(ctx, APICTokenKey) // TokenKey is a constant string representing the key for the token in the database
	if err != nil {
		return APICToken{}, fmt.Errorf("loading token: %w", err)
	}

	if token == "" {
		return APICToken{}, ErrTokenNotFound
	}

	parser := new(jwt.Parser)

	tok, _, err := parser.ParseUnverified(token, jwt.MapClaims{})
	if err != nil {
		return APICToken{}, fmt.Errorf("%w: %s", ErrTokenParse, err)
	}

	claims, ok := tok.Claims.(jwt.MapClaims)
	if !ok {
		return APICToken{}, ErrTokenParse
	}

	expFloat, ok := claims["exp"].(float64)
	if !ok {
		return APICToken{}, fmt.Errorf("%w: exp", ErrTokenMissingClaim)
	}

	exp := time.Unix(int64(expFloat), 0)
	if time.Now().UTC().After(exp.Add(-1 * time.Minute)) {
		return APICToken{}, ErrTokenExpired
	}

	return APICToken{Raw: token, ExpiresAt: exp}, nil

View on GitHub (pinned to 909b515798)

Solutions

  1. Delete/re-register the token: 'cscli capi delete' then 'cscli capi register', or clear the APICTokenKey config item and re-authenticate to store a fresh JWT.
  2. Verify the stored value actually looks like a JWT (three dot-separated base64url segments).
  3. Never hand-edit the token in the database; use cscli commands.

Example fix

// caller: treat parse failure as 'token unusable', force re-auth
tok, err := c.LoadAPICToken(ctx, log)
if errors.Is(err, database.ErrTokenParse) {
    log.Warn("stored CAPI token corrupt, re-authenticating")
    return authenticate(ctx)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// quick structural sanity check on a stored token string before parsing
func looksLikeJWT(tok string) bool {
    parts := strings.Split(tok, ".")
    return len(parts) == 3 && parts[0] != "" && parts[1] != ""
}

Type guard

func isTokenParseErr(err error) bool { return errors.Is(err, database.ErrTokenParse) }

Try / catch

tok, err := client.LoadAPICToken(ctx, log)
if isTokenParseErr(err) {
    log.Warn("stored CAPI token is corrupt, forcing re-authentication")
    return reauthenticate(ctx)
}

Prevention

When it happens

Trigger: Calling LoadAPICToken when the config item contains a corrupt or hand-edited token string; a failed/partial SaveAPICToken wrote garbage; an old token format incompatible with the current parser.

Common situations: Manual editing of the DB/config by an operator; restore from an inconsistent backup; upgrade across versions where token generation changed.

Related errors


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/51ef8f6d8654fa3e. Report an issue: GitHub.