{"record":{"id":"dcf2a841de6c90f2","repo":"apache/pulsar","slug":"failed-to-read-ca-bytes","errorCode":null,"errorMessage":"Failed to read CA bytes","messagePattern":"Failed to read CA bytes","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java","lineNumber":959,"sourceCode":"                    ? this.getWorkerPort() : this.getWorkerPortTls());\n        }\n        return this.workerId;\n    }\n\n    public String getWorkerHostname() {\n        if (isBlank(this.workerHostname)) {\n            this.workerHostname = unsafeLocalhostResolve();\n        }\n        return this.workerHostname;\n    }\n\n    public byte[] getTlsTrustChainBytes() {\n        if (StringUtils.isNotEmpty(getBrokerClientTrustCertsFilePath())\n                && Files.exists(Paths.get(getBrokerClientTrustCertsFilePath()))) {\n            try {\n                return Files.readAllBytes(Paths.get(getBrokerClientTrustCertsFilePath()));\n            } catch (IOException e) {\n                throw new IllegalStateException(\"Failed to read CA bytes\", e);\n            }\n        } else {\n            return null;\n        }\n    }\n\n    public String getWorkerWebAddress() {\n        return String.format(\"http://%s:%d\", this.getWorkerHostname(), this.getWorkerPort());\n    }\n\n    public String getWorkerWebAddressTls() {\n        return String.format(\"https://%s:%d\", this.getWorkerHostname(), this.getWorkerPortTls());\n    }\n\n    public static String unsafeLocalhostResolve() {\n        try {\n            // Get the fully qualified hostname\n            return InetAddress.getLocalHost().getCanonicalHostName();","sourceCodeStart":941,"sourceCodeEnd":977,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/runtime/src/main/java/org/apache/pulsar/functions/worker/WorkerConfig.java#L941-L977","documentation":"WorkerConfig.getTlsTrustChainBytes() lazily loads the TLS trust certificate chain from brokerClientTrustCertsFilePath. If the file exists but cannot be read (permission denied, I/O error, file removed between the exists() check and read), it wraps the IOException in IllegalStateException('Failed to read CA bytes'). Returns null only when the path is empty or the file does not exist.","triggerScenarios":"Accessing getTlsTrustChainBytes() during worker/client construction when brokerClientTrustCertsFilePath is set and the file exists but Files.readAllBytes fails — e.g. unreadable permissions, the path is a directory-like special file, or an I/O error mid-read.","commonSituations":"TLS cert file mounted with root-only permissions while the worker runs as another user; cert file deleted/replaced between exists() and read in a container; wrong file mounted at the configured path (e.g. a symlink to an unreadable target); Kubernetes secret mounted with restrictive mode.","solutions":["Fix filesystem permissions so the worker process user can read brokerClientTrustCertsFilePath (see the wrapped IOException cause).","Verify the configured path points to a readable PEM trust-cert file, not a directory or broken symlink.","Use an unreadable-proof mount mode for the TLS secret (e.g. defaultMode 0444 in Kubernetes).","Alternatively embed the trust chain via a config value instead of a file path if filesystem access is unreliable in your deployment."],"exampleFix":"# before (K8s secret mount)\nvolumes:\n- name: tls-trust\n  secret:\n    secretName: ca-cert\n    defaultMode: 0400   # worker runs as non-root -> unreadable\n# after\nvolumes:\n- name: tls-trust\n  secret:\n    secretName: ca-cert\n    defaultMode: 0444","handlingStrategy":"validation","validationCode":"Path trustCerts = Paths.get(workerConfig.getBrokerClientTrustCertsFilePath());\nif (trustCerts.toString() != null && !trustCerts.toString().isBlank()) {\n    if (!Files.isRegularFile(trustCerts) || !Files.isReadable(trustCerts)) {\n        throw new IllegalStateException(\"TLS trust cert file missing or unreadable: \" + trustCerts);\n    }\n    Files.readAllBytes(trustCerts); // pre-flight read to surface permission issues early\n}","typeGuard":null,"tryCatchPattern":"try {\n    byte[] caBytes = workerConfig.getTlsTrustChainBytes();\n} catch (IllegalStateException e) {\n    if (e.getMessage() != null && e.getMessage().equals(\"Failed to read CA bytes\")) {\n        // check cause IOException: fix file permissions/path of brokerClientTrustCertsFilePath\n    }\n    throw e;\n}","preventionTips":["Mount TLS secrets readable by the worker user (e.g. K8s defaultMode 0444).","Pre-flight check brokerClientTrustCertsFilePath in deployment/entrypoint scripts.","Avoid replacing cert files in place; use atomic rename of fully-readable files.","Keep the exists()-then-read race in mind: mount stable paths, not symlinks to rotating targets."],"tags":["tls","configuration","filesystem","pulsar-worker"],"backgroundTag":"tls-trust-store-unreadable","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}