halo-dev/halo · critical · InvalidConfigurationPropertyValueException

Public key location does not exist

Error message

Public key location does not exist

What it means

Thrown as Spring's InvalidConfigurationPropertyValueException from JwtProperties.readPublicKey during bean construction when halo.security.oauth2.jwt.public-key-location points at a Resource that reports exists()==false. Halo loads RSA keys at startup to sign/verify JWTs, so a missing public key resource is fatal to the security configuration.

Source

Thrown at application/src/main/java/run/halo/app/infra/properties/JwtProperties.java:106

    }

    public void setPrivateKeyLocation(Resource privateKeyLocation) {
        this.privateKeyLocation = privateKeyLocation;
    }

    public RSAPrivateKey getPrivateKey() {
        return privateKey;
    }

    public RSAPublicKey getPublicKey() {
        return publicKey;
    }

    private RSAPublicKey readPublicKey() throws IOException {
        String key = "halo.security.oauth2.jwt.public-key-location";
        Assert.notNull(this.publicKeyLocation, "PublicKeyLocation must not be null");
        if (!this.publicKeyLocation.exists()) {
            throw new InvalidConfigurationPropertyValueException(
                    key, this.publicKeyLocation, "Public key location does not exist");
        }
        try (InputStream inputStream = this.publicKeyLocation.getInputStream()) {
            String source = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
            return RsaKeyConverters.x509().convert(new ByteArrayInputStream(source.getBytes()));
        }
    }

    private RSAPrivateKey readPrivateKey() throws IOException {
        String key = "halo.security.oauth2.jwt.private-key-location";
        Assert.notNull(this.privateKeyLocation, "PrivateKeyLocation must not be null");
        if (!this.privateKeyLocation.exists()) {
            throw new InvalidConfigurationPropertyValueException(
                    key, this.privateKeyLocation, "Private key location does not exist");
        }
        try (InputStream inputStream = this.privateKeyLocation.getInputStream()) {
            String source = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
            return RsaKeyConverters.pkcs8().convert(new ByteArrayInputStream(source.getBytes()));

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Verify the resource path resolves: correct the halo.security.oauth2.jwt.public-key-location value.
  2. Ensure the key file is present at the configured location in the runtime (mount the volume / copy into the image).
  3. Use the classpath: prefix for bundled keys or file: for host-mounted keys, matching how the resource is shipped.
  4. Confirm read permissions for the process user on the key file.

Example fix

# before
halo.security.oauth2.jwt.public-key-location=classpath:jwt-public.pem
# (file not packaged)

# after
halo.security.oauth2.jwt.public-key-location=file:/etc/halo/keys/jwt-public.pem
Defensive patterns

Strategy: validation

Validate before calling

Resource r = resourceLoader.getResource(publicKeyLocation);
if (!r.exists()) {
    throw new IllegalStateException("Public key resource missing: " + publicKeyLocation);
}

Type guard

static boolean resourceExists(Resource r) {
    return r != null && r.exists();
}

Try / catch

try {
    new JwtProperties(issuer, algo, pubRes, privRes);
} catch (InvalidConfigurationPropertyValueException e) {
    // startup-blocking: log, point operator at the public-key-location property, abort boot
    log.error("Configure halo.security.oauth2.jwt.public-key-location to an existing resource", e);
    throw e;
}

Prevention

When it happens

Trigger: Application startup with the property set to a classpath/file/http resource that cannot be resolved (does not exist), e.g. a wrong path, a file not mounted in the container, or a classpath resource absent from the JAR.

Common situations: Docker/K8s deployment where the key volume isn't mounted; typo in the configured path; key file generated on a different node; moving config between environments without copying the key; default dev path pointing at a file that was git-ignored.

Related errors


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