GCP Pub/Sub Delivery Failures: Backlogs, Push Errors, Dead Letters

Your Pub/Sub messages are not arriving where they should. Maybe the backlog keeps growing, your push endpoint returns errors, or messages silently disappear. This page helps you figure out exactly where the pipeline is broken (publisher, subscription, subscriber, permissions, or retry configuration) and fix it.

A “message delivery failure” means a message was published to a topic but never successfully reached and was acknowledged by the subscriber. The message might be stuck in a backlog, rejected by a push endpoint, redelivered repeatedly, or routed to a dead-letter topic. Each of these patterns has a different root cause and a different fix.

If you are new to Pub/Sub, start with the Pub/Sub Overview to understand topics, subscriptions, and how delivery works. This page assumes you have a topic and at least one subscription already created.

How Pub/Sub delivery works in plain English

When Pub/Sub is healthy, the flow looks like this: your application publishes a message to a topic. Every subscription attached to that topic gets its own copy of the message. If the subscription uses pull, the subscriber fetches the message and acknowledges it. If the subscription uses push, Pub/Sub sends the message via HTTP POST to an endpoint, and a 2xx response counts as acknowledgement. Either way, once acknowledged, the message is done. For a deeper comparison of these delivery modes, see Pub/Sub Push vs Pull.

Think of it like a postal service

The publisher is the sender dropping a letter in the mailbox. The topic is the post office that sorts and copies the letter for each recipient. The subscription is the delivery route. The subscriber is the person who opens the letter and confirms they got it. If nobody opens the letter (no ack), the postal service keeps trying. If the address is wrong (push endpoint error), the letter bounces. If too many deliveries fail, the letter goes to the dead-letter office.

A backlog is the set of messages that have been published but not yet acknowledged. A small backlog is normal. A backlog that keeps growing means your subscriber cannot keep up, is not running, or is failing to acknowledge messages.

Pull failures happen when the subscriber is not pulling, not acknowledging, or losing the ack race against the deadline. Push failures happen when the HTTP endpoint returns an error, is unreachable, or rejects the request due to authentication. The symptoms and fixes are different for each.

A dead-letter topic is a safety net. After a message fails delivery approximately a configured number of times, Pub/Sub forwards it to the dead-letter topic instead of retrying forever. Without one, messages that can never be processed expire silently after the retention window.

First question to answer

Are messages not being published at all, or are they published but not consumed? These are completely different problems with completely different fixes. Check topic/send_message_operation_count first to find out which side is broken.

The delivery pipeline at a glance

Every Pub/Sub message follows this path. When delivery fails, exactly one stage in this pipeline is broken:

  1. Publisher sends a message to a topic
  2. The topic fans out the message to every attached subscription
  3. The subscription delivers to the subscriber (pull) or push endpoint (push)
  4. The subscriber processes the message and sends an acknowledgement
  5. If acknowledgement fails repeatedly, the message goes to the dead-letter topic (if configured) or stays in the backlog until retention expires

Understanding this pipeline helps you narrow the problem. A zero publish count means step 1 is broken. A growing backlog with active publishing means steps 3–5 are broken. The Pub/Sub Messaging Model page explains the fan-out and ordering mechanics in detail.

Use this page when…

  • Your subscription backlog keeps growing and never drains
  • Your pull subscriber appears healthy but messages repeat endlessly
  • Your push endpoint returns 403, 404, or 500 errors
  • Messages are landing in your dead-letter topic
  • Published messages never appear in the subscription at all
  • The oldest unacked message age keeps rising
  • Your subscriber processes messages but the backlog does not shrink
  • A Cloud Function or Cloud Run service triggered by Pub/Sub is not receiving events

Fast triage checklist

Use this table to jump straight to the most likely cause based on what you are seeing:

SymptomLikely failing componentWhat to check firstFix direction
Zero send_message_operation_countPublisherPublisher logs, client errors, network, quotasFix the publishing application
Backlog growing, no pull activityPull subscriberIs the subscriber running? Check process/pod statusStart or redeploy the subscriber
Backlog growing, subscriber runningAck failureAck deadline vs processing time, ack logic in codeIncrease ack deadline or fix ack logic
Push returns 403IAM / authService agent roles/run.invoker, push auth configGrant missing IAM role
Push returns 404Endpoint URLEndpoint URL correctness, service deployment statusFix URL or redeploy the service
Push returns 5xxEndpoint applicationEndpoint logs, application errors, resource limitsFix the endpoint application
Messages in dead-letter topicSubscriber processingDead-letter message content, subscriber error logsFix processing logic, then reprocess
Published messages never appearTopic-subscription bindingSubscription attached to the correct topic, subscription not expiredVerify or recreate the subscription

Check monitoring first

Before changing any configuration, check Cloud Monitoring metrics. These tell you exactly where the pipeline is broken:

# List all available Pub/Sub metrics for your project
gcloud monitoring metric-descriptors list \
  --filter="metric.type:pubsub.googleapis.com" \
  --format="value(type)"

Key metrics and what they tell you

  • topic/send_message_operation_count. How many messages your publisher is sending. Zero means no messages are being published and the problem is on the publisher side, not the subscriber side.

  • subscription/num_undelivered_messages. The current backlog size. A number that keeps growing means subscribers are falling behind or not running.

  • subscription/oldest_unacked_message_age. The age (in seconds) of the oldest unacknowledged message. A high and rising number means the subscriber is stalled. A stable low number means delivery is healthy.

  • subscription/push_request_count. Push delivery attempts broken down by HTTP response code. High counts at non-2xx codes mean your push endpoint is rejecting messages.

  • subscription/pull_request_count. Pull requests from subscribers. Zero means no subscriber is pulling.

  • subscription/dead_letter_message_count. Messages forwarded to the dead-letter topic. A growing count means your subscriber consistently fails to process certain messages.

You can set up alerting policies on these metrics so you catch delivery failures before the backlog becomes critical. For custom counters based on log patterns, see log-based metrics.

# Quick check: see the current backlog and oldest message age
gcloud pubsub subscriptions describe my-subscription \
  --format="table(name,numUndeliveredMessages,messageRetentionDuration)"

# Check push delivery response codes in the last hour
gcloud monitoring time-series list \
  --filter='metric.type="pubsub.googleapis.com/subscription/push_request_count"
    AND resource.labels.subscription_id="my-push-subscription"' \
  --interval-start-time=$(date -u -d '-1 hour' +%Y-%m-%dT%H:%M:%SZ) \
  --format="table(metric.labels.response_code, points.value.int64Value)"

Publisher-side delivery failures

If topic/send_message_operation_count is zero or dropping, messages are not being published. The problem is in your publishing application, not Pub/Sub.

Messages not being published at all

Check your publisher application logs first. Common causes:

  • Publisher client not initialised or not connected. Verify the Pub/Sub client library is creating a publisher successfully and that the topic name is correct. A typo in the topic name causes a NOT_FOUND error on every publish.

  • Authentication failure. The service account running the publisher needs roles/pubsub.publisher on the topic. Missing credentials or wrong service account produce PERMISSION_DENIED errors. See troubleshooting authentication errors for credential diagnostics.

  • Pub/Sub API not enabled. The Pub/Sub API must be enabled in the project. A PERMISSION_DENIED or SERVICE_DISABLED error on publish calls means the API is off.

# Check if the Pub/Sub API is enabled
gcloud services list --enabled --filter="name:pubsub.googleapis.com"

# Verify the topic exists
gcloud pubsub topics describe my-topic

# Check IAM policy on the topic
gcloud pubsub topics get-iam-policy my-topic
Quick test

Run gcloud pubsub topics publish my-topic —message=“hello” from your terminal. If it succeeds, Pub/Sub and the topic are fine. The problem is in your publisher code. If it fails, the error message tells you exactly what is wrong (wrong topic name, missing API, missing permissions).

Publisher timeouts and throughput issues

  • DEADLINE_EXCEEDED errors. The publish call timed out before Pub/Sub confirmed receipt. This can happen under network pressure, high message volume, or when the publisher machine is CPU/memory constrained. Increase the publish timeout in your client configuration or reduce batch size.

  • Quota limits. Pub/Sub has per-project quotas on publish throughput. If you hit them, publish calls return RESOURCE_EXHAUSTED. Check your quota usage in the Cloud Console under IAM & Admin > Quotas.

  • Large messages. Pub/Sub has a 10 MB maximum message size. Messages exceeding this limit are rejected. If your payloads are large, store the data in Cloud Storage and publish a reference instead.

How to confirm publish is working

# Publish a test message and confirm it succeeds
gcloud pubsub topics publish my-topic \
  --message="test message $(date -u +%Y-%m-%dT%H:%M:%SZ)"

# If this succeeds, your topic is fine and the problem is in your publisher code
# If this fails, read the error message — it tells you what is wrong

Pull subscription failures

Pull subscriptions require the subscriber to actively fetch messages. If nothing is pulling, messages accumulate in the backlog until the retention window expires (default 7 days).

No active subscriber

The most basic pull failure: no process is pulling from the subscription. Check whether your subscriber application, container, or Cloud Function is actually running. A crashed pod, a stopped VM, or an undeployed function means zero pull activity and a growing backlog.

# Manually pull messages to confirm the subscription works
gcloud pubsub subscriptions pull my-subscription \
  --limit=5 \
  --auto-ack

# If messages come back, the subscription is fine — your subscriber is not pulling
# Pull without acknowledging (messages redeliver after ack deadline)
gcloud pubsub subscriptions pull my-subscription \
  --limit=5

Subscriber not acknowledging messages

Your subscriber pulls messages and processes them, but never sends an acknowledgement. Pub/Sub redelivers unacknowledged messages after the ack deadline expires. This creates a loop: the same messages arrive again and again, the backlog never drains, and you see duplicate processing in your logs.

Fix: ensure every code path that successfully processes a message calls ack(). Check error handling paths too. A caught exception that skips the ack causes silent redelivery.

Ack deadline too short

The 10-second trap

The default ack deadline is only 10 seconds. If your subscriber does anything slower than that (a database write, an HTTP call, image processing), Pub/Sub assumes the message was lost and sends it again. You end up processing the same message multiple times while the backlog never shrinks. This is the most confusing failure because everything looks like it is working.

If your processing involves database writes, HTTP calls to slow APIs, or heavy computation, the deadline expires before processing finishes. Pub/Sub assumes the message was not processed and redelivers it.

# Check the current ack deadline
gcloud pubsub subscriptions describe my-subscription \
  --format="value(ackDeadlineSeconds)"

# Increase the ack deadline to 60 seconds
gcloud pubsub subscriptions update my-subscription \
  --ack-deadline=60

For variable processing times, use modifyAckDeadline in your client code to extend the deadline dynamically while processing is still in progress.

Slow processing causing backlog growth

If messages arrive faster than your subscriber can process them, the backlog grows even though everything is technically working. Solutions: increase the number of subscriber instances, increase concurrency within each subscriber, or optimise processing time.

Missing IAM permissions

The service account running your subscriber needs roles/pubsub.subscriber on the subscription. Without it, every pull request fails with PERMISSION_DENIED.

# Check who has subscriber access
gcloud pubsub subscriptions get-iam-policy my-subscription

# Grant subscriber role to your service account
gcloud pubsub subscriptions add-iam-policy-binding my-subscription \
  --member="serviceAccount:my-sa@my-project.iam.gserviceaccount.com" \
  --role="roles/pubsub.subscriber"

Push subscription failures

Push subscriptions deliver messages via HTTP POST to an endpoint URL. Pub/Sub retries with exponential backoff on any non-success response. If the endpoint consistently returns errors, the backlog grows and push_request_count shows high error rates.

Understanding push response codes

Pub/Sub treats responses 200, 201, 202, 204, and 102 as successful delivery. Everything else is a failure that triggers a retry:

  • 403 Forbidden: almost always an IAM issue. The Pub/Sub service agent does not have permission to invoke the endpoint. This is the most common push failure for private Cloud Run and Cloud Functions endpoints.

  • 404 Not Found: the endpoint URL is wrong, the service is not deployed, or the route does not exist. Verify the URL and confirm the service is running.

  • 401 Unauthorized: push authentication is configured but the endpoint is rejecting the JWT token. Check that the audience in the push config matches what your endpoint expects.

  • 5xx Server Error: your endpoint application is crashing. Check the endpoint logs (not Pub/Sub logs) to find the application error. Use Logs Explorer filtered to your Cloud Run or Cloud Functions service.

  • Timeout / unreachable: the endpoint did not respond within the push timeout, or Pub/Sub cannot reach it. Check firewall rules, VPC configuration, and whether the endpoint is publicly accessible (or correctly configured for internal access).

Push errors are silent from the Pub/Sub side

Pub/Sub does not log individual push delivery failures in Cloud Logging by default. If your push endpoint is returning errors, you will only see them in the push_request_count metric or in your endpoint’s own logs. Do not assume “no errors in Pub/Sub logs” means delivery is healthy.

# Check the push endpoint URL and auth configuration
gcloud pubsub subscriptions describe my-push-subscription \
  --format="yaml(pushConfig)"

# Update the push endpoint URL
gcloud pubsub subscriptions modify-push-config my-push-subscription \
  --push-endpoint=https://my-service-abc123-uc.a.run.app/pubsub

Push authentication and JWT issues

When push authentication is enabled, Pub/Sub includes a signed JWT in the Authorization header. Your endpoint must validate this token. Common problems:

  • The audience in the push config does not match what the endpoint checks
  • The service account specified in the push config does not exist or is disabled
  • The endpoint validates the token issuer but uses the wrong expected value

Private Cloud Run and Cloud Functions endpoints

This is the single most common push delivery failure. If your Cloud Run service or Cloud Function requires authentication (the default), the Pub/Sub service agent must have roles/run.invoker (for Cloud Run) or roles/cloudfunctions.invoker (for Cloud Functions) granted on the service. Without it, every push returns 403. For more on how Cloud Run authentication works, see the Cloud Run Security Model guide.

# Find your project number (needed for the service agent email)
gcloud projects describe PROJECT_ID --format="value(projectNumber)"

# Grant Pub/Sub permission to invoke a private Cloud Run service
gcloud run services add-iam-policy-binding my-cloud-run-service \
  --region=us-central1 \
  --member="serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com" \
  --role="roles/run.invoker"

# Grant Pub/Sub permission to invoke a Cloud Function (gen2)
gcloud functions add-invoker-policy-binding my-function \
  --region=us-central1 \
  --member="serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com"
Note

The Pub/Sub service agent email follows the pattern service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com. Use the numeric project number, not the project ID. These are different values.

Firewall and endpoint reachability

Push delivery comes from Google-owned IP ranges. If your endpoint is behind a firewall or in a VPC with restrictive ingress rules, Pub/Sub cannot reach it. For Cloud Run and Cloud Functions with default settings, this is not an issue. For custom endpoints behind a load balancer or on a VM, ensure the firewall allows HTTPS traffic from Google’s IP ranges.

Reading push failure patterns from logs

Pub/Sub itself does not log individual push attempts in Cloud Logging by default. To diagnose push failures:

  1. Check push_request_count metric grouped by response_code to see the pattern
  2. Check the endpoint service logs (Cloud Run, Cloud Functions, or your own server) for the corresponding errors
  3. If your endpoint is a Cloud Function triggered by Pub/Sub, see debugging Cloud Functions failures for log query guidance
  4. Enable audit logging for Pub/Sub if you need to trace individual delivery attempts

Dead-letter topics and retry behaviour

What dead-letter topics do

A dead-letter topic is a separate topic that receives messages your subscription could not deliver after repeated attempts. Instead of retrying forever until the retention window expires, Pub/Sub forwards the message to the dead-letter topic so you can inspect it, fix the problem, and reprocess.

Maximum delivery attempts are approximate

When you set —max-delivery-attempts=10, Pub/Sub forwards the message after approximately 10 failed deliveries. The actual count may be slightly higher or lower due to the distributed nature of the system. Do not design logic that depends on an exact attempt count.

IAM requirements for dead-lettering

Dead-lettering fails silently without IAM

If the Pub/Sub service agent does not have the correct permissions, dead-lettering quietly does nothing. Messages stay in the backlog as if no dead-letter topic exists. There is no error message. You only discover it when you notice the dead-letter subscription has zero messages while the source backlog keeps growing.

The Pub/Sub service agent needs two permissions:

  • roles/pubsub.publisher on the dead-letter topic (to publish forwarded messages)
  • roles/pubsub.subscriber on the source subscription (to acknowledge forwarded messages)
# Create a dead-letter topic and a monitoring subscription
gcloud pubsub topics create my-subscription-dead-letter
gcloud pubsub subscriptions create my-dead-letter-monitor \
  --topic=my-subscription-dead-letter

# Configure dead-letter policy on the source subscription
gcloud pubsub subscriptions update my-subscription \
  --dead-letter-topic=my-subscription-dead-letter \
  --max-delivery-attempts=10

# Grant the service agent permission to publish to the dead-letter topic
gcloud pubsub topics add-iam-policy-binding my-subscription-dead-letter \
  --member="serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com" \
  --role="roles/pubsub.publisher"

# Grant the service agent permission to ack on the source subscription
gcloud pubsub subscriptions add-iam-policy-binding my-subscription \
  --member="serviceAccount:service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com" \
  --role="roles/pubsub.subscriber"

Retry policy vs push backoff

Pub/Sub has two separate retry mechanisms that are easy to confuse:

  • Retry policy (configured on the subscription): controls the minimum and maximum delay between redelivery attempts. Applies to both pull and push subscriptions. Default is immediate redelivery with no delay.

  • Push backoff: an automatic exponential backoff Pub/Sub applies to push endpoints that return errors. The backoff increases with consecutive failures and can reach minutes between attempts. This happens on top of any configured retry policy.

For pull subscriptions, the retry policy adds delay between redeliveries. For push subscriptions, the exponential backoff from consecutive failures is the dominant delay. You can configure the retry policy to add a minimum backoff for pull redeliveries:

# Set a retry policy with minimum 10s and maximum 600s backoff
gcloud pubsub subscriptions update my-subscription \
  --min-retry-delay=10s \
  --max-retry-delay=600s

When to use a dead-letter topic

Configure a dead-letter topic when:

  • Message loss is unacceptable and you need to capture every failed message for investigation
  • Some messages are “poison pills” that will never process successfully and would block other messages
  • You want to separate healthy delivery from failed delivery for monitoring purposes

Inspecting dead-lettered messages

# Pull messages from the dead-letter monitoring subscription
gcloud pubsub subscriptions pull my-dead-letter-monitor \
  --limit=10 \
  --format="table(message.data.decode(base64), message.attributes)"

# Each dead-lettered message includes these attributes:
# - CloudPubSubDeadLetterSourceSubscription: which subscription forwarded it
# - CloudPubSubDeadLetterSourceDeliveryCount: approximate delivery attempts

Read the message body and the source subscription attribute. This tells you which subscription failed and what the message contained. Fix the subscriber, then decide whether to replay the dead-lettered messages or reprocess them manually.

Recover after the fix

After you fix the root cause, verify that delivery is actually recovering:

Confirm the backlog is draining

# Watch the backlog size decrease over time
gcloud pubsub subscriptions describe my-subscription \
  --format="value(name, numUndeliveredMessages)"

# Check oldest unacked message age — it should be dropping

The num_undelivered_messages metric should trend down and oldest_unacked_message_age should stabilise or drop. If neither changes, the fix did not work or there is a second problem.

Validate push delivery recovery

After fixing a push endpoint, check push_request_count grouped by response code. You should see 2xx responses increasing and error responses dropping. Push backoff may cause a delay before Pub/Sub ramps delivery back up after a prolonged failure period.

Verify subscriber acknowledgements

Check subscription/ack_message_count in Cloud Monitoring. A healthy subscriber shows a steady ack rate that matches or exceeds the publish rate.

Decide whether to replay messages

If messages were lost during the outage or if you need to reprocess messages from a specific point in time, Pub/Sub supports seek operations. You can seek to a timestamp to replay all messages published after that time, or seek to a snapshot to replay from a saved state. Use seek carefully because it redelivers messages that were already successfully processed. Your subscriber must handle duplicates safely.

# Seek to a timestamp to replay messages from that point forward
gcloud pubsub subscriptions seek my-subscription \
  --time="2026-03-27T10:00:00Z"

# Create a snapshot for future replay points
gcloud pubsub snapshots create my-snapshot \
  --subscription=my-subscription

Push vs pull failure patterns

Doorbell vs mailbox

Push is like a doorbell: Pub/Sub walks up to your endpoint and rings. If nobody answers (non-2xx), Pub/Sub walks away and comes back later, waiting longer each time. Pull is like a mailbox: messages sit there until your subscriber checks. If nobody checks, the mailbox overflows. The failure modes are different because the delivery model is different.

Pull subscriptionPush subscription
How failure appearsBacklog grows, pull_request_count at zero or lowBacklog grows, push_request_count shows non-2xx codes
Where errors show upSubscriber application logs, Cloud Monitoring pull metricsPush response code metrics, endpoint service logs
Common root causesSubscriber not running, not acking, ack deadline too short, missing roles/pubsub.subscriberEndpoint returning errors, missing roles/run.invoker, wrong URL, auth misconfiguration
Retry behaviourMessages redeliver after ack deadline expires; retry policy adds configurable delayAutomatic exponential backoff on consecutive errors; can slow to minutes between attempts
Best first diagnostic actionManually gcloud pubsub subscriptions pull to verify messages existCheck push_request_count by response code to identify the HTTP error pattern

For a full comparison of when to use each delivery type, see Pub/Sub Push vs Pull.

Checking subscription configuration

Many delivery failures trace back to misconfigured subscriptions. Check these fields:

# Describe a subscription — inspect all key fields
gcloud pubsub subscriptions describe my-subscription

# Key fields to check:
# - topic: the topic this subscription receives from
# - pushConfig.pushEndpoint: URL for push subscriptions (empty for pull)
# - ackDeadlineSeconds: how long before unacked messages are redelivered
# - messageRetentionDuration: how long undelivered messages are kept
# - deadLetterPolicy: dead-letter topic and max delivery attempts
# - retryPolicy: min and max backoff for redelivery

# List all subscriptions attached to a topic
gcloud pubsub topics list-subscriptions my-topic

Common mistakes

  1. Not acknowledging messages after processing. This is the most common cause of a growing backlog. Unacknowledged messages are redelivered after the ack deadline, creating a loop of duplicate processing. Ensure every successful processing path calls ack(), including inside error-handling branches that swallow exceptions.

  2. Not configuring a dead-letter topic. Without one, messages that repeatedly fail processing are silently lost after the retention window expires. For any pipeline where message loss matters, configure a dead-letter topic and monitor it.

  3. Forgetting to grant the Pub/Sub service agent roles/run.invoker for private push endpoints. Every push delivery to a private Cloud Run or Cloud Functions service fails with 403 unless the service agent has the invoker role. This is the single most common push delivery failure.

  4. Setting the ack deadline too short for the actual processing time. If your processing takes 30 seconds but the ack deadline is 10 seconds, Pub/Sub redelivers the message twice before processing finishes. Increase the deadline or use modifyAckDeadline to extend it dynamically.

  5. Assuming the dead-letter attempt count is exact. The —max-delivery-attempts value is approximate. Do not build logic that expects exactly N attempts before dead-lettering. Design your subscriber to be idempotent so that extra delivery attempts do not cause problems.

  6. Forgetting IAM grants for dead-letter forwarding. The Pub/Sub service agent needs roles/pubsub.publisher on the dead-letter topic and roles/pubsub.subscriber on the source subscription. Without both, dead-lettering silently fails and messages stay in the backlog forever.

  7. Blaming the subscriber when the publisher is broken. Always check topic/send_message_operation_count first. If no messages are being published, fixing the subscriber does nothing. Separate publisher-side from subscriber-side failures before debugging.

How to confirm it is fixed

After applying your fix, verify these indicators over the next 15–30 minutes:

  • num_undelivered_messages is trending down (backlog draining)
  • oldest_unacked_message_age has stabilised or is dropping
  • push_request_count shows 2xx responses (for push subscriptions)
  • dead_letter_message_count has stopped growing
  • ack_message_count shows steady acknowledgements matching the publish rate
  • Subscriber application logs show successful processing without repeated errors

If the backlog is not draining after 15 minutes, there may be a second issue or the fix did not fully resolve the problem. Re-check the triage table and metrics.

Frequently asked questions

How do I tell whether Pub/Sub delivery failures are on the publisher side or subscriber side?

Check the Cloud Monitoring metric topic/send_message_operation_count. If it shows zero, no messages are being published and the problem is your publisher. If messages are being published normally, check subscription/num_undelivered_messages. A growing backlog means your subscriber is not consuming or acknowledging messages. These two metrics separate publisher-side from subscriber-side failures within seconds.

What happens to Pub/Sub messages that fail delivery repeatedly?

Without a dead-letter topic, messages stay in the subscription backlog until the retention window expires (default 7 days, configurable up to 31 days), then they are permanently lost. If you configure a dead-letter topic with a max delivery attempts limit, messages that exceed approximately that many delivery attempts are forwarded to the dead-letter topic instead of expiring silently. The delivery attempt count is approximate, not exact.

Why does my push subscription keep getting 403 errors from Cloud Run?

The Pub/Sub service agent needs roles/run.invoker on your Cloud Run service. The service agent email is service-PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com (use the numeric project number, not the project ID). This is the most common cause of persistent 403 errors on push delivery to private Cloud Run services. Grant the role and push delivery resumes within seconds.

How do I stop Pub/Sub from redelivering messages my subscriber already processed?

The most common cause of redelivery is that your subscriber processes messages but does not acknowledge them, or the ack deadline expires before processing finishes. Ensure every successfully processed message gets an explicit ack. If processing takes longer than the default 10-second ack deadline, increase it with --ack-deadline on the subscription or extend it dynamically with modifyAckDeadline in your client code.

How do I inspect messages that landed in my dead-letter topic?

Create a subscription on the dead-letter topic, then pull messages from it. Each message retains its original data and attributes, plus Pub/Sub adds a CloudPubSubDeadLetterSourceSubscription attribute identifying which subscription forwarded it. Read the message body and attributes to understand the failure pattern, then fix the root cause in your subscriber before replaying or reprocessing.

Last verified: 27 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.