halo-dev/halo · warning · InvalidCookieException

Cookie token was not Base64 encoded; value was '{}'

Error message

Cookie token was not Base64 encoded; value was '{}'

What it means

decodeCookie() Base64-decodes the raw cookie value (after re-padding to a multiple of 4). If Base64.getDecoder().decode throws IllegalArgumentException, the cookie is not valid Base64 and an InvalidCookieException is thrown. This is the first validation gate before the payload is split into tokens.

Source

Thrown at application/src/main/java/run/halo/app/security/authentication/rememberme/TokenBasedRememberMeServices.java:309

    }

    protected long calculateExpireTime(ServerWebExchange exchange, Authentication authentication) {
        var tokenLifetime = rememberMeCookieResolver.getCookieMaxAge().toSeconds();
        return Instant.now().plusSeconds(tokenLifetime).toEpochMilli();
    }

    protected String[] decodeCookie(String cookieValue) throws InvalidCookieException {
        int paddingCount = 4 - (cookieValue.length() % 4);
        if (paddingCount < 4) {
            char[] padding = new char[paddingCount];
            Arrays.fill(padding, '=');
            cookieValue += new String(padding);
        }
        String cookieAsPlainText;
        try {
            cookieAsPlainText = new String(Base64.getDecoder().decode(cookieValue.getBytes()));
        } catch (IllegalArgumentException ex) {
            throw new InvalidCookieException("Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
        }
        String[] tokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, DELIMITER);
        for (int i = 0; i < tokens.length; i++) {
            tokens[i] = URLDecoder.decode(tokens[i], StandardCharsets.UTF_8);
        }
        return tokens;
    }

    /**
     * Inverse operation of decodeCookie.
     *
     * @param cookieTokens the tokens to be encoded.
     * @return base64 encoding of the tokens concatenated with the ":" delimiter.
     */
    protected String encodeCookie(String[] cookieTokens) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < cookieTokens.length; i++) {
            sb.append(URLEncoder.encode(cookieTokens[i], StandardCharsets.UTF_8));

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Clear the bad cookie in the browser (the server also cancels it via handleError -> cancelCookie) and log in again.
  2. Confirm no other application shares the remember-me cookie name/path.
  3. Check that a reverse proxy is not URL-rewriting or truncating the cookie value.
  4. Ensure encodeCookie/decodeCookie pair is from the same Halo version on all nodes.
Defensive patterns

Strategy: try-catch

Try / catch

// The framework already handles this in handleError -> cancelCookie:
.onErrorResume(InvalidCookieException.class, ex -> {
    log.debug("Non-Base64 remember-me cookie rejected");
    return Mono.empty();
})

Prevention

When it happens

Trigger: A remember-me cookie value containing non-Base64 characters, stripped padding that the re-pad logic cannot fix (e.g., truncated mid-group), or a value that was never Base64-encoded (a cookie set by another application on the same domain/name). Reached for every auto-login attempt via autoLogin -> decodeCookie.

Common situations: Another app overwrote the cookie name; a reverse proxy stripped characters; the cookie was hand-edited; switching cookie-encoding schemes between versions.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/010e89959f061d64. Report an issue: GitHub.