tymondesigns/jwt-auth · error · Tymon\JWTAuth\Exceptions\TokenInvalidException

Wrong number of segments

Error message

Wrong number of segments

What it means

Thrown by TokenValidator::validateStructure() (via new Token($value) / TokenValidator::check) when the input string split on '.' does not yield exactly three segments. It is the first structural gate before parsing: a JWT must be header.payload.signature, so the value presented is not even shaped like a JWT.

Source

Thrown at src/Validators/TokenValidator.php:40

     * @return string
     */
    public function check($value)
    {
        return $this->validateStructure($value);
    }

    /**
     * @param  string  $token
     * @return string
     *
     * @throws \Tymon\JWTAuth\Exceptions\TokenInvalidException
     */
    protected function validateStructure($token)
    {
        $parts = explode('.', $token);

        if (count($parts) !== 3) {
            throw new TokenInvalidException('Wrong number of segments');
        }

        $parts = array_filter(array_map('trim', $parts));

        if (count($parts) !== 3 || implode('.', $parts) !== $token) {
            throw new TokenInvalidException('Malformed token');
        }

        return $token;
    }
}

View on GitHub (pinned to 6c70930a92)

Solutions

  1. Log the raw Authorization header (or parser input source) and confirm what is actually being sent - in most cases it is 'undefined', 'null', or empty
  2. Fix the client so it only attaches a real token: check the token exists before setting the header (if (token) ...), and send only the token after 'Bearer ', never the scheme itself
  3. If the client legitimately has no token, let it hit your guest flow instead of attaching a garbage header
  4. Check the configured input parsers (config 'parsers' order: Authorization header, query string, cookie) - a stale query param or cookie can override the good header on routes where you didn't expect it

Example fix

// before - axios interceptor attaches whatever is in storage, even undefined
axios.interceptors.request.use(cfg => { cfg.headers.Authorization = 'Bearer ' + store.token; return cfg; });

// after - only attach when a token exists
axios.interceptors.request.use(cfg => {
  if (store.token) cfg.headers.Authorization = 'Bearer ' + store.token;
  return cfg;
});
Defensive patterns

Strategy: type-guard

Validate before calling

// Reject non-JWT-shaped input at the boundary before the parser stack runs
function looksLikeJwt(?string $token): bool
{
    return $token !== null
        && substr_count($token, '.') === 2
        && preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $token) === 1;
}

// usage
if (!looksLikeJwt($request->bearerToken())) {
    return response()->json(['error' => 'token_not_provided'], 401);
}

Type guard

function looksLikeJwt(?string $token): bool
{
    return is_string($token)
        && preg_match('/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/', $token) === 1;
}

Try / catch

use Tymon\JWTAuth\Exceptions\TokenInvalidException;

try {
    $user = auth('api')->parseToken()->authenticate();
} catch (TokenInvalidException $e) {
    return response()->json(['error' => 'token_invalid'], 401);
}

Prevention

When it happens

Trigger: Passing a value with no dots (the literal strings 'null', 'undefined', 'Bearer', a database id) or too many dots (a JWE token with 5 parts, a doubly-joined token) to JWTAuth::parseToken(), auth:api middleware, or JWTAuth::getToken()->get(). Typically the Authorization header, query param, or cookie contained something other than a JWT.

Common situations: Frontend sends 'Authorization: Bearer undefined' or 'Bearer null' because the auth store was empty at request time; token variable never set before an API call; client sends the whole 'Bearer <token>' string including the scheme as the token value; passing a Laravel encrypted-cookie value or an opaque session id where a JWT was expected.

Related errors


AI-assisted analysis of tymondesigns/jwt-auth@6c70930a92 (2026-08-21). Data as JSON: /api/errors/f872f5b86da68946. Report an issue: GitHub.