Checks/OPT_IDLE_003

NATS Inactive Consumer: What It Means and How to Fix It

Severity
Info
Category
Consistency
Applies to
Idle Resources
Check ID
OPT_IDLE_003
Detection threshold
delivered_stream_seq unchanged across time range

An inactive consumer is a JetStream consumer that has made no delivery progress — zero new messages delivered — across the observed time range. Its delivered_stream_seq remains unchanged throughout the evaluation period. The consumer exists in the server’s metadata, holds a Raft group (if replicated), but no application is actively pulling or receiving messages from it.

Why this matters

Every JetStream consumer carries operational cost. A durable consumer maintains its delivered sequence, ack state, and configuration in the server’s metadata. For replicated consumers (R3), each one runs its own Raft group with leader elections, heartbeats, and state replication across three servers. A handful of forgotten consumers are harmless. Dozens or hundreds of them create real overhead — more Raft groups mean more CPU spent on consensus, more memory for tracking state, and larger meta snapshots that slow down server restarts.

The more insidious problem is retention interaction. Streams configured with an interest-based retention policy only delete messages once all consumers have acknowledged them. An inactive consumer that never acknowledges anything effectively pins the stream’s storage at its high-water mark. The stream keeps growing, retention never fires, and the operator sees disk usage climbing without an obvious cause. This is one of the most common “mystery disk growth” scenarios in JetStream deployments.

Inactive consumers also create operational noise. When operators run nats consumer report to understand their system, pages of inactive consumers obscure the active ones that actually matter. During incidents, this clutter slows diagnosis. During capacity planning, it inflates asset counts and makes the system look busier than it is.

Common causes

  • Application was retired without cleaning up its consumer. A service that consumed from a stream was decommissioned, but nobody deleted the durable consumer it created. This is the most common cause by far, especially in organizations without a formal consumer lifecycle process.

  • Consumer was created for testing or debugging. An operator created a consumer to inspect stream contents during an investigation, then forgot to delete it afterward. Test consumers with names like test-1 or debug-consumer are a strong signal.

  • Push consumer with no active subscriber. A push consumer has a deliver subject configured, but no application is subscribed to that subject. The consumer exists and has messages pending, but nothing is receiving them. See also: OPT_SYS_004 (Unbound Push Consumer).

  • Consumer filter subject no longer matches any published subjects. The consumer’s filter subject was valid when created, but publishers have since changed their subject hierarchy. The consumer is technically active but will never receive new messages because nothing publishes to subjects it cares about.

  • Deployment failure left the consumer orphaned. A new version of a service was deployed with a different consumer name (or on a different stream), but the old consumer was never cleaned up. The old consumer sits idle while the new one handles the workload.

How to diagnose

List consumers and identify inactive ones

Start with a report across all consumers on a stream:

Terminal window
nats consumer report ORDERS

This shows each consumer’s delivered count, ack pending, unprocessed messages, and last activity. Consumers with zero unprocessed messages and no recent last activity timestamp are likely inactive.

For a broader view across all streams:

Terminal window
nats stream report --consumers

Inspect a specific consumer

Get detailed state for a suspect consumer:

Terminal window
nats consumer info ORDERS my-consumer

Key fields to check:

  • Delivered Stream Sequence — If this hasn’t changed over multiple checks, the consumer is inactive.
  • Num Pending — Messages waiting to be delivered. If nonzero but delivered sequence is stale, the subscriber isn’t connected.
  • Num Ack Pending — Messages delivered but not acknowledged. Zero with a stale delivered sequence means the consumer has simply stopped.
  • Last Activity — When the consumer last had any client interaction.

Check if anyone is subscribed (push consumers)

For push consumers, verify whether anyone is listening on the deliver subject:

Terminal window
nats server report connections --subscriptions --filter-subject "deliver.ORDERS.my-consumer"

If no connections are subscribed to the deliver subject, the push consumer is unbound and effectively inactive.

Cross-reference with the stream’s retention policy

Terminal window
nats stream info ORDERS

If the stream uses interest-based retention, an inactive consumer blocks message deletion. Check whether the stream’s first sequence is advancing or stuck.

How to fix it

Immediate: remove consumers you know are unused

If you’ve confirmed a consumer is no longer needed, delete it:

Terminal window
nats consumer delete ORDERS my-consumer

For bulk cleanup, list and filter:

Terminal window
nats consumer ls ORDERS -j | jq -r '.[] | select(.num_pending == 0) | .name'

Review the output and delete consumers that are confirmed inactive. Don’t blindly delete — a consumer with zero pending may simply be caught up and actively waiting for new messages.

Short-term: set inactive thresholds on ephemeral consumers

Ephemeral consumers support an inactive_threshold that automatically deletes the consumer after a period of no client activity:

1
// Go - nats.go
2
js, _ := nc.JetStream()
3
4
_, err := js.Subscribe("orders.>", handler,
5
nats.InactiveThreshold(10*time.Minute),
6
nats.ManualAck(),
7
)
1
# Python - nats.py
2
from nats.js.api import ConsumerConfig
3
4
await js.subscribe(
5
"orders.>",
6
cb=handler,
7
config=ConsumerConfig(inactive_threshold=600), # seconds
8
)

This prevents future ephemeral consumers from becoming permanently inactive. It doesn’t help with existing durable consumers, which must be explicitly deleted.

Long-term: implement consumer lifecycle governance

Name consumers with ownership metadata. Use a naming convention that encodes the owning service and environment: orderprocessor-prod-v2 instead of consumer1. When a consumer’s owning service is decommissioned, you know which consumers to clean up.

Audit consumers as part of service decommissioning. Add a step to your deployment runbook: when retiring a service, list its consumers and delete them. This is cheaper than discovering them months later during a storage investigation.

Use Synadia Insights for continuous monitoring. Insights evaluates consumer activity across your entire deployment automatically. Instead of running manual nats consumer report commands, inactive consumers surface as findings without any operator intervention — across every stream, every account, every collection cycle.

Frequently asked questions

How long does a consumer need to be idle before it’s considered inactive?

Synadia Insights flags consumers whose delivered_stream_seq hasn’t changed across the selected time range. The exact duration depends on your observation window — typically hours to days. A consumer that hasn’t delivered a single message in 24 hours on a stream that’s actively receiving publishes is almost certainly inactive. A consumer on a stream that only receives messages weekly may be fine. Context matters.

Can an inactive consumer cause disk growth on an interest-retention stream?

Yes. Interest-based retention deletes messages only after all defined consumers have acknowledged them. An inactive consumer never acknowledges anything, so messages are never deleted. This is the most operationally dangerous side effect of inactive consumers — it can fill disks silently. If you use interest-based retention, auditing consumer activity is not optional.

What’s the difference between an inactive consumer and a drained consumer?

An inactive consumer has stopped making delivery progress entirely — its delivered sequence is frozen. A drained consumer (OPT_IDLE_004) is caught up: it has processed all available messages and has zero pending, but the stream itself is also inactive. A drained consumer was doing its job; there’s just nothing left to do. An inactive consumer may have stopped for a reason that needs investigation.

Should I delete inactive consumers automatically?

For ephemeral consumers, yes — use inactive_threshold to let the server handle cleanup automatically. For durable consumers, be more careful. Durable consumers track delivery state that an application may rely on when it restarts. Deleting a durable consumer means the application will need to re-create it, potentially reprocessing messages from the stream’s first available sequence. Verify with the owning team before deleting durable consumers.

Does an inactive consumer consume server resources?

Yes. Every consumer, active or not, uses memory for its state tracking. Replicated consumers (R3) maintain a Raft group with ongoing heartbeat traffic and leader election participation. The per-consumer overhead is small individually, but it compounds. Clusters with thousands of inactive consumers can see measurable impact on meta snapshot times and Raft consensus performance.

Proactive monitoring for NATS inactive consumer with Synadia Insights

With 100+ always-on audit Checks from the NATS experts, Insights helps you find and fix problems before they become costly incidents.
No alert rules to write. No dashboards to maintain.

Start a 14-day Insights trial
Cancel