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
- Clear the bad cookie in the browser (the server also cancels it via handleError -> cancelCookie) and log in again.
- Confirm no other application shares the remember-me cookie name/path.
- Check that a reverse proxy is not URL-rewriting or truncating the cookie value.
- 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
- Do not set other applications' cookies under the same name/path as Halo's remember-me cookie.
- Verify proxies preserve the full Base64 cookie value verbatim.
- Use the same encode/decode implementation across all nodes.
- If migrating cookie formats, expire old cookies deliberately rather than letting them fail.
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
- Cannot resolve shared dependency ${root} from ${resolutionBa
- Shared dependency ${root} resolved to ${packageJson.name ||
- Cookie contained signature '{}' but expected '{}'
- Cookie token[1] did not contain a valid number (contained '{
- ESM UI provider output must expose a default PluginModule ex
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/010e89959f061d64.
Report an issue: GitHub.