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
- Verify the resource path resolves: correct the halo.security.oauth2.jwt.public-key-location value.
- Ensure the key file is present at the configured location in the runtime (mount the volume / copy into the image).
- Use the classpath: prefix for bundled keys or file: for host-mounted keys, matching how the resource is shipped.
- 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
- Smoke-test key resource existence in your deploy script before starting Halo.
- Keep public/private key resources under version-controlled secrets management.
- Use absolute paths or classpath: resources consistently across environments.
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
- Private key location does not exist
- ESM UI provider output must expose a default PluginModule ex
- ESM UI provider output must contain at most one entry styles
- Cannot resolve shared dependency ${root} from ${resolutionBa
- Shared dependency ${root} resolved to ${packageJson.name ||
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/1a1357567bbf1afd.
Report an issue: GitHub.