halo-dev/halo · critical · InvalidConfigurationPropertyValueException
Private key location does not exist
Error message
Private key location does not exist
What it means
Thrown as InvalidConfigurationPropertyValueException from JwtProperties.readPrivateKey during bean construction when halo.security.oauth2.jwt.private-key-location points at a Resource whose exists()==false. The private key (PKCS#8 PEM) is required to sign issued JWTs, so startup cannot proceed.
Source
Thrown at application/src/main/java/run/halo/app/infra/properties/JwtProperties.java:119
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
- Correct halo.security.oauth2.jwt.private-key-location to the actual resource location.
- Mount/copy the PKCS#8 PEM private key into the runtime at the configured path.
- Use classpath: for packaged keys or file: for mounted secrets.
- Confirm the file is in PKCS#8 DER/PEM form and readable by the process.
Example fix
# before halo.security.oauth2.jwt.private-key-location=file:./halo.pem # after halo.security.oauth2.jwt.private-key-location=file:/etc/halo/keys/jwt-private.pem
Defensive patterns
Strategy: validation
Validate before calling
Resource r = resourceLoader.getResource(privateKeyLocation);
if (!r.exists()) {
throw new IllegalStateException("Private key resource missing: " + privateKeyLocation);
} Type guard
static boolean resourceExists(Resource r) {
return r != null && r.exists();
} Try / catch
try {
new JwtProperties(issuer, algo, pubRes, privRes);
} catch (InvalidConfigurationPropertyValueException e) {
log.error("Configure halo.security.oauth2.jwt.private-key-location to an existing PKCS#8 resource", e);
throw e;
} Prevention
- Mount secrets volumes in containers and verify at deploy time.
- Store keys outside the image; never bake production keys into the JAR.
- Validate key resource paths in CI against the target environment.
When it happens
Trigger: Startup with the private-key property set to a non-existent Resource: wrong path, unmounted volume, missing classpath entry, or a file not deployed to the container.
Common situations: Container deployment missing the mounted secrets volume; typo in path; key generated elsewhere and not copied; environment-specific path differences; file git-ignored from the build.
Related errors
- Public 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/7b96d56390154e3e.
Report an issue: GitHub.