jwtk/jjwt · error · IllegalStateException
Password has been destroyed. Password character array may…
Error message
Password has been destroyed. Password character array may not be obtained.
What it means
PasswordSpec implements Destroyable: after destroy() is called the internal char array is zeroed. Any subsequent call to toCharArray() (via assertActive()) throws IllegalStateException because the password is no longer usable or readable.
Solutions
- Do not reuse a Password after destroying it; create a new Password instance from the source credentials.
- Restructure code so the char[] is consumed before destroy(), e.g. derive keys inside the usage scope.
- Catch IllegalStateException around toCharArray() to detect destroyed credentials and fail gracefully.
Example fix
// before Password p = Keys.password(chars); derive(p); p.destroy(); derive(p); // IllegalStateException // after Password p = Keys.password(chars); derive(p); p.destroy(); // re-create if needed Password p2 = Keys.password(sourceChars);
Defensive patterns
Strategy: try-catch
Validate before calling
if (password instanceof Destroyable d && d.isDestroyed()) {
throw new IllegalStateException("Password already destroyed");
} Type guard
boolean usable(Password p) { return p instanceof Destroyable d && !d.isDestroyed(); } Try / catch
try {
char[] chars = password.toCharArray();
// use chars
} catch (IllegalStateException e) {
// password destroyed: recreate from source credentials
} Prevention
- Treat destroy() as terminal — never reuse the instance
- Scope Password usage tightly (try-with-resources)
- Track destroyed credentials and recreate when needed
When it happens
Trigger: Calling password.toCharArray() after password.destroy() has been invoked.
Common situations: Long-lived Password references destroyed by try-with-resources or security cleanup code, then accidentally reused later in the same request; caching a Password in a field and destroying it at logout.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- getEncoded() is disabled for Password instances as they are…
- EC JWK x,y coordinates do not exist on elliptic curve
- Invalid ECDSA signature format.
- [JWA RFC 7518, Section…
- JWT signature does not match locally computed signature…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/07abbb92df8ec0b5.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/PasswordSpec.java:46
private static final String ENCODED_DISABLED_MSG =
"getEncoded() is disabled for Password instances as they are intended to be used " +
"with key derivation algorithms only. Because passwords rarely have the length or entropy " +
"necessary for secure cryptographic operations such as authenticated hashing or encryption, " +
"they are disabled as direct inputs for these operations to help avoid accidental misuse; if " +
"you see this exception message, it is likely that the associated Password instance is " +
"being used incorrectly.";
private volatile boolean destroyed;
private final char[] password;
public PasswordSpec(char[] password) {
Assert.notEmpty(password, "Password character array cannot be null or empty.");
this.password = password.clone(); // ensures changes to the source array do not change this instance
}
private void assertActive() {
if (destroyed) {
throw new IllegalStateException(DESTROYED_MSG);
}
}
@Override
public char[] toCharArray() {
assertActive();
return this.password.clone();
}
@Override
public String getAlgorithm() {
return NONE_ALGORITHM;
}
@Override
public String getFormat() {
return null; // encoding isn't supported, so we return null per the Key#getFormat() JavaDoc
}View on GitHub (pinned to fb71496164)