Home / OCI

OCITroubleshootingIntermediate

OCI Streaming: Kafka clients can't connect or authenticate - bootstrap servers, SASL usernames and auth tokens

Fix Kafka producers and consumers that fail against OCI Streaming with SaslAuthenticationException, broker-not-available or unknown topic errors.

Deepak Bhardwaj7 min read

Applies toOCI Streaming (Kafka-compatible API), Apache Kafka Java clients, librdkafka-based clients, OCI CLI 3.x

A Kafka producer or consumer pointed at OCI Streaming fails at startup. It logs SaslAuthenticationException: Authentication failed, or Connection to node -1 ... could not be established. Broker may not be available, or it connects and then reports that the topic is not in metadata. Nearly every case comes down to five things: the bootstrap endpoint, the SASL username string, the auth token, the network path, or the stream name.

Symptom to cause

Client error Likely cause Fix
SaslAuthenticationException: Authentication failed Wrong username format, wrong stream pool OCID, revoked or mistyped auth token, missing IAM policy Rebuild the username string, create a new token, check the policy
Authentication failed even though the credentials look right Two streams with the same name in the same compartment (in different pools) Use unique stream names, or the correct pool OCID
Broker may not be available / timeouts Wrong region endpoint, port 9092 blocked, private stream pool reached from outside its VCN Use the pool's bootstrap server, open egress on 9092, check the route
Topic ... not present in metadata / UNKNOWN_TOPIC_OR_PARTITION Stream doesn't exist in that pool, or it isn't ACTIVE Create the stream in the pool you authenticate against
RecordTooLargeException Message or batch larger than the service accepts Cap max.request.size at 1 MB and keep messages small
Random disconnects under load Connections recycled by the service's load balancing Set retries on the producer

Check 1: the stream pool and its bootstrap server

Every Kafka connection authenticates against a stream pool, not a single stream. Find the pool and read its Kafka settings:

# List stream pools in a compartment
oci streaming admin stream-pool list \
  --compartment-id ocid1.compartment.oc1..aaaa... \
  --query 'data[].{name:name, id:id, state:"lifecycle-state"}' --output table

# Show the pool's Kafka settings and endpoint details
oci streaming admin stream-pool get \
  --stream-pool-id ocid1.streampool.oc1.iad.aaaa...

For a public pool, the bootstrap server is the regional endpoint on port 9092, for example streaming.us-ashburn-1.oci.oraclecloud.com:9092. If the pool has a private endpoint, use the bootstrap server shown in the pool's Kafka connection settings in the Console. Don't build it by hand. The pool's region must match the endpoint region. A client using the Phoenix endpoint for an Ashburn pool fails.

Then confirm the stream exists in that pool and is ACTIVE:

oci streaming admin stream list \
  --compartment-id ocid1.compartment.oc1..aaaa... \
  --stream-pool-id ocid1.streampool.oc1.iad.aaaa... \
  --query 'data[].{name:name, state:"lifecycle-state", partitions:partitions}' --output table

The Kafka topic name is the stream name.

Check 2: the SASL username string

OCI Streaming uses SASL/PLAIN over TLS (SASL_SSL). Oracle documents the username as:

<tenancyName>/<domain>/<username>/<streamPoolOcid>

Oracle's own client examples also show the shorter <tenancyName>/<username>/<streamPoolOcid> form. Which one works depends on how your tenancy and users are set up (identity domains, legacy IDCS federation). If one form fails with a known-good token, try the other before you change anything else.

Get the exact pieces rather than typing them from memory:

# Tenancy name (not the OCID)
oci iam tenancy get --tenancy-id "$OCI_TENANCY" --query 'data.name' --raw-output

# Your user name as OCI stores it (often an email address)
oci iam user get --user-id ocid1.user.oc1..aaaa... --query 'data.name' --raw-output

Common mistakes:

  • Using the tenancy OCID instead of the tenancy name.
  • Using the stream OCID instead of the stream pool OCID.
  • Leaving out the domain segment for a user in a non-default identity domain.
  • A trailing space or newline in the username or token, picked up from a secret store or environment file.

Tip

Test the string with kcat before you touch the application. If kcat can list metadata, the problem is in the application's configuration.

kcat -L \
  -b streaming.us-ashburn-1.oci.oraclecloud.com:9092 \
  -X security.protocol=SASL_SSL \
  -X sasl.mechanisms=PLAIN \
  -X sasl.username='mytenancy/Default/svc-kafka@example.com/ocid1.streampool.oc1.iad.aaaa...' \
  -X sasl.password='<auth-token>'

Check 3: the auth token and IAM policy

The password is an auth token for the user, not the Console password and not an API signing key.

oci iam auth-token list --user-id ocid1.user.oc1..aaaa... \
  --query 'data[].{desc:description, state:"lifecycle-state", created:"time-created"}' --output table

A token is shown only once, when you create it. If nobody knows where the value came from, create a new one, update the secret, and delete the old one after the clients switch over. Each user can have only a few auth tokens, so check the current limit and delete unused ones first.

Warning

Deleting an auth token immediately breaks every client still using it. Roll the new token out to every producer and consumer first, then delete the old one.

The user, or a group they belong to, needs permission on the stream. Least-privilege statements look like this:

Allow group KafkaProducers to use stream-push in compartment Streaming
Allow group KafkaConsumers to use stream-pull in compartment Streaming

stream-push covers producing (PutMessages). stream-pull covers consuming, including cursors and consumer group commits. Admin tooling that lists or creates topics through the Kafka Admin API needs more, typically manage stream-family scoped to the right compartment. A missing policy usually shows up as an authentication failure, not a separate authorization error.

To rule out Kafka-client problems, test the same identity with the native API:

EP=$(oci streaming admin stream get --stream-id ocid1.stream.oc1.iad.aaaa... \
  --query 'data."messages-endpoint"' --raw-output)

CURSOR=$(oci streaming stream cursor create-cursor \
  --stream-id ocid1.stream.oc1.iad.aaaa... --partition 0 --type TRIM_HORIZON \
  --endpoint "$EP" --query 'data.value' --raw-output)

oci streaming stream message get --stream-id ocid1.stream.oc1.iad.aaaa... \
  --cursor "$CURSOR" --endpoint "$EP" --limit 5

If this works with the CLI profile of the same user but Kafka fails, the problem is the username string or the token.

Check 4: the network path

# Can we reach the broker port at all?
nc -vz streaming.us-ashburn-1.oci.oraclecloud.com 9092

# Does TLS complete?
openssl s_client -connect streaming.us-ashburn-1.oci.oraclecloud.com:9092 -brief </dev/null
Client location What to check
OCI instance in a private subnet, public pool NAT gateway or service gateway route, egress rule for TCP 9092
OCI instance, private pool Same VCN or a routed/peered VCN, NSG on the pool's private endpoint allows TCP 9092 from the client
On-premises Firewall egress to 9092, proxy that doesn't intercept TLS, FastConnect/VPN routes for private pools
OKE pod Node subnet egress, pod network policy, DNS resolution of the bootstrap name

A private pool's endpoint can't be reached from the internet. A laptop on the corporate VPN without a route to that VCN times out. It won't get an authentication error.

Check 5: client settings that fit the service

Oracle's recommended Java settings:

properties.put("bootstrap.servers", "streaming.us-ashburn-1.oci.oraclecloud.com:9092");
properties.put("security.protocol", "SASL_SSL");
properties.put("sasl.mechanism", "PLAIN");
properties.put("sasl.jaas.config",
  "org.apache.kafka.common.security.plain.PlainLoginModule required "
  + "username=\"mytenancy/Default/svc-kafka@example.com/ocid1.streampool.oc1.iad.aaaa...\" "
  + "password=\"" + authToken + "\";");
// Producer
properties.put("retries", 5);                       // transient errors and connection recycling
properties.put("max.request.size", 1024 * 1024);    // 1 MB
// Consumer
properties.put("max.partition.fetch.bytes", 1024 * 1024);

Streaming doesn't implement every Kafka feature. Kafka Streams, log compaction, transactions, idempotent producers and dynamic partition addition aren't supported. A client with enable.idempotence=true or a transactional ID fails or misbehaves. Recent Kafka Java clients turn idempotence on by default, so set enable.idempotence=false explicitly. Check the current Streaming limits for per-partition throughput and message size before you size partitions.

Prevent it next time

  • Store the full SASL username and the token together in OCI Vault. Load both from there and trim whitespace.
  • Use a dedicated service user per application, with stream-push or stream-pull scoped to the stream's compartment.
  • Track auth token age and rotate tokens on a schedule, with an overlap window so clients never lose access.
  • Keep stream names unique per compartment, so the Kafka-side name always maps to one stream.
  • Add a kcat -L smoke test to the deployment pipeline that runs against the real pool before the app starts.