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

  1. Do not reuse a Password after destroying it; create a new Password instance from the source credentials.
  2. Restructure code so the char[] is consumed before destroy(), e.g. derive keys inside the usage scope.
  3. 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

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


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)