apache/pulsar · error · RuntimeException

Failed to read token from file

Error message

Failed to read token from file

What it means

TokenAuthenticationV5 supports reading the client token from a file:// URI. The file-backed token supplier reads all bytes of the file on each get(); an IOException (missing file, permissions, directory instead of file) is rethrown as a RuntimeException with this message, wrapping the original IOException.

Source

Thrown at pulsar-client/src/main/java/org/apache/pulsar/client/impl/auth/v5/TokenAuthenticationV5.java:135

        }
    }

    /** A supplier reading the token from a file on every call, so a rotated token is picked up. */
    public static final class FileTokenSupplier implements Supplier<String>, Serializable {

        private static final long serialVersionUID = 3160666668166028760L;
        private final URI uri;

        public FileTokenSupplier(final URI uri) {
            this.uri = uri;
        }

        @Override
        public String get() {
            try {
                return new String(Files.readAllBytes(Paths.get(uri)), StandardCharsets.UTF_8).trim();
            } catch (IOException e) {
                throw new RuntimeException("Failed to read token from file", e);
            }
        }
    }

    private final Supplier<String> tokenSupplier;

    // Late-bound at initializeAsync(...): the client's bounded blocking executor, onto which the token()
    // read is off-loaded so a file-backed supplier (Files.readAllBytes) never runs on the Netty event loop
    // (PIP-478). Null when used outside a client, in which case the read runs inline.
    private transient volatile Executor blockingExecutor;

    /**
     * @param tokenSupplier supplies the current token on each call (enables refresh without rebuild)
     */
    public TokenAuthenticationV5(Supplier<String> tokenSupplier) {
        this.tokenSupplier = tokenSupplier;
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the token file path exists and is readable by the process user before starting the client (ls/permissions).
  2. Fix the URI in the auth configuration (token file parameter) to the correct absolute path.
  3. In Kubernetes, confirm the projected volume/secret is mounted and populated before the client starts; add an initContainer wait if needed.
  4. If tokens rotate by replacement, ensure the path always resolves (symlink swap, not delete+create).

Example fix

// before
AuthParams: token:file:///etc/pulsar/token          (file missing)
// after
AuthParams: token:file:///var/run/secrets/pulsar/token  (verified mounted & readable)
Defensive patterns

Strategy: try-catch

Validate before calling

java.nio.file.Path p = java.nio.file.Path.of(uri.getPath());
if (!java.nio.file.Files.isRegularFile(p) || !java.nio.file.Files.isReadable(p)) {
    throw new IllegalStateException("token file missing or unreadable: " + p);
}

Try / catch

try {
    String token = tokenSupplier.get();
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().equals("Failed to read token from file")) {
        // check path/mount/permissions, then re-init auth
    } else throw e;
}

Prevention

When it happens

Trigger: Configuring authentication with tokenFromFile / a file: URI where the path does not exist, is not readable, or is a directory; the token file being deleted or rotated out from under a running client that calls get() lazily.

Common situations: Kubernetes secret mounts not yet projected at client startup; wrong path in authParams (typos, container vs host paths); file permissions changed after deployment; token file removed by a secret rotation job.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/3da0edb87058cdc6. Report an issue: GitHub.