floci-io/floci · error · IllegalStateException
Failed to write self-signed TLS certificate
Error message
Failed to write self-signed TLS certificate
What it means
Thrown by AcknowledgeJob when the supplied nonce does not match the nonce stored on the job. Every job returned by PollForJobs carries a one-time nonce; AcknowledgeJob must echo it back verbatim to prove the caller received the job from a poll. A mismatch means the nonce is stale, mistyped, or belongs to a different job.
Source
Thrown at src/main/java/io/github/hectorvent/floci/config/TlsConfigSource.java:205
List<String> allSans = new ArrayList<>();
allSans.addAll(DEFAULT_SAN_HOSTNAMES);
allSans.addAll(customHostnames);
CertificateGenerator gen = new CertificateGenerator();
CertificateGenerator.GeneratedCertificate generated = gen.generateSelfSignedCertificate(
"localhost",
allSans,
KeyAlgorithm.RSA_2048);
Files.writeString(certFile, generated.certificatePem());
Files.writeString(keyFile, generated.privateKeyPem());
LOG.infov("TLS: generated self-signed certificate: {0}", certFile);
// Persist metadata for change detection on restart
persistMetadata(tlsDir, allSans);
} catch (IOException e) {
throw new IllegalStateException("Failed to write self-signed TLS certificate", e);
}
}
private static void validateFileExists(String path, String description) {
if (!Files.isReadable(Path.of(path))) {
throw new IllegalStateException(
description + " file not found or not readable: " + path);
}
}
/**
* Returns {@code true} if the certificate at {@code certFile} is genuinely self-signed
* (issuer == subject) and therefore usable as a trust anchor. Legacy Floci certs carried a
* cosmetic Amazon issuer DN and return {@code false} here, triggering regeneration on upgrade.
*/
private boolean isSelfSigned(Path certFile) {
try {
X509Certificate cert = new CertificateGenerator().parseCertificate(Files.readString(certFile));View on GitHub (pinned to 62ff490619)
Solutions
- Always take the nonce from job.nonce of the exact job object returned by PollForJobs, keyed by the same jobId
- If the job may already be acknowledged, expect InvalidNonce/InvalidJobState on retry and treat it as idempotent success
- Verify no other worker instance has already consumed and acknowledged the polled job
Example fix
// before client.acknowledgeJob(r -> r.jobId(jobId).nonce(nonceFromConfig)); // after Job job = polledJobs.get(0); client.acknowledgeJob(r -> r.jobId(job.getId()).nonce(job.getNonce()));
Defensive patterns
Strategy: validation
Validate before calling
Job job = polled.getJobs().get(0);
if (job.getNonce() == null || job.getNonce().isBlank()) {
throw new IllegalStateException("polled job missing nonce; do not acknowledge");
}
client.acknowledgeJob(r -> r.jobId(job.getId()).nonce(job.getNonce())); Prevention
- Never hand-build nonces; always pass through job.nonce from the same poll response
- Keep jobId and nonce as one immutable pair in worker state to avoid cross-wiring
When it happens
Trigger: Calling AcknowledgeJob with a nonce from a different jobId; re-acknowledging after the job's nonce was rotated; manually constructing the nonce instead of reading job.nonce from the polled Job object; string truncation or whitespace when copying the nonce.
Common situations: Worker restarts that replay an old poll response; multiple worker processes consuming the same queue and mixing up job/nonce pairs; logging frameworks that trim or mask the nonce before it is reused.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- ${description} file not found or not readable: ${path}
- MethodNotAllowedException
- TLS enabled but no certificate provided and self-signed gene
- BadRequestException
- ValidationException
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/c0f2aac1a7a13171.
Report an issue: GitHub.