{"record":{"id":"2746cb29c27c4aab","repo":"floci-io/floci","slug":"description-file-not-found-or-not-readable-p","errorCode":null,"errorMessage":"${description} file not found or not readable: ${path}","messagePattern":"(.+?) file not found or not readable: (.+?)","errorType":"console","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/github/hectorvent/floci/config/TlsConfigSource.java","lineNumber":211,"sourceCode":"                    \"localhost\",\n                    allSans,\n                    KeyAlgorithm.RSA_2048);\n\n            Files.writeString(certFile, generated.certificatePem());\n            Files.writeString(keyFile, generated.privateKeyPem());\n\n            LOG.infov(\"TLS: generated self-signed certificate: {0}\", certFile);\n\n            // Persist metadata for change detection on restart\n            persistMetadata(tlsDir, allSans);\n        } catch (IOException e) {\n            throw new IllegalStateException(\"Failed to write self-signed TLS certificate\", e);\n        }\n    }\n\n    private static void validateFileExists(String path, String description) {\n        if (!Files.isReadable(Path.of(path))) {\n            throw new IllegalStateException(\n                    description + \" file not found or not readable: \" + path);\n        }\n    }\n\n    /**\n     * Returns {@code true} if the certificate at {@code certFile} is genuinely self-signed\n     * (issuer == subject) and therefore usable as a trust anchor. Legacy Floci certs carried a\n     * cosmetic Amazon issuer DN and return {@code false} here, triggering regeneration on upgrade.\n     */\n    private boolean isSelfSigned(Path certFile) {\n        try {\n            X509Certificate cert = new CertificateGenerator().parseCertificate(Files.readString(certFile));\n            return cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal());\n        } catch (Exception e) {\n            LOG.warnv(\"TLS: could not inspect existing certificate ({0}); regenerating\", e.getMessage());\n            return false;\n        }\n    }","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/floci-io/floci/blob/62ff490619e7bd3554597c28c704081b4c15add5/src/main/java/io/github/hectorvent/floci/config/TlsConfigSource.java#L193-L229","documentation":"Thrown by AcknowledgeJob when the nonce is valid but the job's stored status is not 'Created'. Once a job is acknowledged its status moves to 'InProgress', so a second AcknowledgeJob for the same job is rejected with InvalidJobStateException. This mirrors AWS, where acknowledgment is a one-time transition out of the Created state.","triggerScenarios":"Calling AcknowledgeJob twice for the same jobId; a worker retry after a network timeout where the first acknowledge actually succeeded; two worker instances picking up the same job (nonce shared) and both acknowledging.","commonSituations":"At-least-once retry logic in the worker that does not remember prior acknowledgments; duplicated messages in the worker's internal queue; SDK client retries enabled at the HTTP layer causing invisible double submit.","solutions":["Treat InvalidJobStateException with 'already been acknowledged' as success in the worker's retry path (idempotency by design)","Persist the acknowledged jobId locally before retrying so the worker skips re-acknowledgment","Ensure only one worker instance consumes each polled job"],"exampleFix":"// before\nclient.acknowledgeJob(r -> r.jobId(jobId).nonce(nonce));\n\n// after\ntry {\n    client.acknowledgeJob(r -> r.jobId(jobId).nonce(nonce));\n} catch (InvalidJobStateException alreadyAcked) {\n    // first attempt went through; safe to continue processing\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    client.acknowledgeJob(r -> r.jobId(jobId).nonce(nonce));\n} catch (InvalidJobStateException e) {\n    if (e.getMessage().contains(\"already been acknowledged\")) {\n        logger.info(\"job {} acknowledged by earlier attempt\", jobId); // treat as success\n    } else throw e;\n}","preventionTips":["Record acknowledged jobIds durably before processing so retries skip AcknowledgeJob","Run one worker per job partition to prevent double acknowledgment"],"tags":["codepipeline","acknowledgejob","idempotency","job-state"],"backgroundTag":null,"analyzedSha":"62ff490619e7bd3554597c28c704081b4c15add5","analyzedAt":"2026-08-14T14:25:23.764Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}