python

Tracking Celery Task Failures in Python

Nehemiah Bosire

Nehemiah Bosire on

Tracking Celery Task Failures in Python

Whenever you place an order on Amazon (or any other e-commerce site for that matter), you get that “order placed successfully” notification almost instantly. But did you know that there’s much more to the whole experience than meets the eye?

In Python applications, Celery is the major driver behind the whole thing. The tasks that take time are queued and sent to brokers. Workers pick them up and process them, and the application still runs normally, independent of what is going on in the background.

In this guide, we’ll explain how Celery task failures behave and how you can keep track of everything that goes wrong. We will reproduce a task failure through an example and observe the entries in worker logs. Then, we will use AppSignal to track the failure with full context.

Celery as a Default for Background Jobs in Python

Celery is often in charge of some time-consuming tasks like processing huge datasets or calling external APIs. It plays a huge role in why websites feel so fast. You know how you get an instant success message whenever you place an order or submit a form? Well, there’s a lot of heavy lifting going on behind the scenes.

However, the main problem with Celery is that background tasks fail quietly. From the outside, everything will look healthy because it will manage to succeed after a couple of retries. However, if you take a closer look, you’ll see that multiple errors occurred before this success. In fact, teams generally discover these failures only when a user asks why a certain action didn’t go through.

Celery uses default worker logs to report errors, which is fine when your system is small, but once you’ve got tons of tasks running across multiple workers and queues, it all gets messy fast. Suddenly, engineering teams are left squinting at lines of text, trying to figure out what happened.

The Problem with Celery’s Defaults

Celery’s defaults make background jobs easy to run, but they can also hide reliability issues. A task may fail several times and move through retries before it eventually succeeds, so from the outside everything looks fine while the real errors stay hidden in worker logs. To understand how these defaults can impact your system's stability, let’s look at a practical example of how Celery handles a failing task.

Celery Runs, Fails, and Retries

Celery has built-in retry mechanisms that automatically rerun tasks after an exception. In our application example, we reproduced a task that fails intentionally most of the time:

Python
#A task that fails often @app.task(bind=True, max_retries=3) def fragile_task(self): attempt = self.request.retries + 1 retries_left = self.max_retries - self.request.retries print( f"fragile_task attempt {attempt} " f"(task_id={self.request.id}, retries_left={retries_left})" ) time.sleep(1) if random.random() < 0.7: raise self.retry(exc=Exception("Simulated failure"), countdown=2) return "Success"

Our task is designed to fail 70% of the time, with a maximum of three retries before it finally gives up. So when we trigger the task using our producer.py script, Celery will send the job to the message broker (RabbitMQ in our case) and the worker will begin to execute it.

This is our producer.py code:

Python
from tasks import add, fragile_task # Send a simple task result1 = add.delay(4, 6) print("Add task sent:", result1.id) # Send several fragile tasks for i in range(5): result = fragile_task.delay() print("Flaky task sent:", result.id)

At this point, the worker begins processing tasks in the background.

Exceptions Go to Logs Nobody Watches

Celery comes in handy when offloading work to background workers. The flow is simple: you push a task to a queue, the worker picks it up, and your web app stays fast. However, the downside is that Celery failures can be easy to miss.

When a task fails, the traceback just gets printed in worker process logs. So, unless you are actively tailing those logs, there is a good chance they’ll stay under the radar. There’s no central place where you can see failures or track trends over time.

Let's do a test run and look at the logs the worker produces:

Celery worker logs showing task retries and simulated failure exceptions
Celery worker logs showing task retries and simulated failure exceptions

Here, we see the worker connect to the RabbitMQ broker (amqp://guest@127.0.0.1:5672) and register two tasks: tasks.add and tasks.fragile_task.

The first one, add, executes immediately and succeeds.

Next, the worker receives several fragile_tasks jobs, which are tasks that we intentionally made unstable with a 70% chance of failing. So when a failure occurs, Celery logs the exception, such as fragile_task attempt 1, and schedules a retry (Retry in 2s: Exception('Simulated failure')). After retrying, most tasks eventually succeed, which is why you later see succeeded in ~1s: 'Success' .

If you look closely at the test run above, you’ll notice the worker logs include entries like this:

Shell
[2026-03-04 19:27:10,301: INFO/MainProcess] Task tasks.fragile_task[5cc33856-7038-4d54-9952-f7236c0accf9] received [2026-03-04 19:27:10,303: INFO/MainProcess] Task tasks.fragile_task[7173fb28-6ea9-4517-a5fc-d8035600fde8] received [2026-03-04 19:27:10,303: INFO/ForkPoolWorker-2] Task tasks.fragile_task[167b7c9e-7d46-4792-b7ab-d4dd0fbd4e6b] retry: Retry in 2s: Exception('Simulated failure') [2026-03-04 19:27:10,303: INFO/ForkPoolWorker-3] Task tasks.fragile_task[7173fb28-6ea9-4517-a5fc-d8035600fde8] retry: Retry in 2s: Exception('Simulated failure') [2026-03-04 19:27:10,303: INFO/ForkPoolWorker-10] Task tasks.fragile_task[5cc33856-7038-4d54-9952-f7236c0accf9] retry: Retry in 2s: Exception('Simulated failure')

From these logs, we can see that the task failed multiple times before reaching its retry limit. Although this information technically exists, it is buried deep in the worker logs. As the system scales and multiple workers run at the same time, the logs quickly become noisy and difficult to interpret.

Retries Hide Real Failures

A task might fail a few times, retry automatically, and eventually succeed. For an untrained eye, this looks like a win. But there are exceptions behind the scenes that you didn’t see. For example, a task that has failed four times and succeeded in its fifth attempt has already raised four exceptions, consumed worker time, and caused a delay for other jobs.

You are unable to see the failures unless you take the time to dig through the worker logs. But this is not practical, especially when you’re running a distributed system with a massive user base that generates loads of log data.

You Find Something Is Broken From Users

Sometimes, a background job fails silently for some users, and retries fail to fix it, as well. However, you only realize this days later, after a user complains about a payment that didn’t go through or a confirmation email that never arrived.

And that’s the issue with Celery and the model it follows. It simply raises an exception and forgets about it, which is good for throughput, but it can cause failures to slip by under the radar.

What AppSignal Captures

In the previous section, we intentionally created failing tasks and triggered them. While the worker logs showed retries and exceptions, we had to manually track task IDs across multiple log lines to understand what actually happened.

This is where AppSignal is useful. It automatically instruments Celery tasks and records every execution for each run. Instead of digging through messy logs, it groups and displays task failures in a structured way, making it easy to see what went wrong and why.

Automatic Task Instrumentation

We’ve already seen how demanding it is to go through worker logs when undertaking investigations. In a distributed system, it’s almost impossible since you are supposed to manually scan thousands of lines of worker logs. AppSignal addresses this issue by removing the need to sift through those.

Once it’s installed, all background tasks are immediately instrumented. It’s capable of gathering structured data regarding each task's execution, including retry attempts, exceptions, and performance metrics. Now, you can see clearly how background jobs are behaving.

Exceptions With Full Tracebacks

By now, AppSignal has been installed, and you’ve set it up to monitor your Python application. You can immediately see how quickly it provides a full trace of task retries.

AppSignal records the complete traceback alongside rich contextual information. You no longer have to dig through logs manually. It shows you exactly what went wrong; you’ve got the exception type, stack trace, when the error occurred, and which worker executed the task. Errors are also grouped by exception type, which makes recurring problems easier to spot.

Retry Visibility

Logs can get pretty messy, even in a simple app, but imagine what it would look like in a distributed system that has thousands of tasks, each generating tons of worker logs and going through multiple retries.

AppSignal makes keeping track of all that a breeze. It shows important details like how many retries happened, when they occurred, and exactly where the error popped up in your code. Without it, a task that fails a few times before finally succeeding might look totally fine on the surface, even though it’s actually failing repeatedly behind the scenes.

Task Context and Arguments

As a software engineer, you already understand the significance of context. When debugging a failure, most of us want to know why something happened and whether it was caused by a new change or something else entirely.

AppSignal helps you out by recording task arguments that led to a failure. This allows you to correlate errors with specific inputs while automatically filtering sensitive information. When you can see both the exception and task context together, debugging becomes much faster.

Installing AppSignal

Enabling Celery monitoring with AppSignal is pretty simple: you only need to install it once in the application environment and initialize it when the worker starts.

The full setup process is covered in the AppSignal Python installation guide, which explains how to:

  • Install the AppSignal package
  • Configure your API key
  • Start the application with monitoring enabled

Seeing Failures as They Happen

In distributed systems, task failures often get buried in worker logs. A task may eventually succeed after a few retries, making the issue seem temporary. But when a task fails repeatedly across multiple workers, the real problem can easily go unnoticed.

AppSignal surfaces these failures immediately, as it captures exceptions directly from the worker process. Instead of hunting through logs across multiple machines, it allows you to view failures as structured events.

In this example, we’ve instrumented the Celery worker with AppSignal and triggered several failing fragile_task executions to demonstrate how it all works.

Celery Task Failures Appear in the Errors Dashboard

When the failing tasks run, AppSignal catches the exceptions and bundles identical failures together. Instead of spamming you with a log entry for every retry, it aggregates them into a single issue on the dashboard.

This makes it easy to spot a recurring problem, as you can see in this AppSignal dashboard:

AppSignal dashboard displaying grouped task errors
AppSignal dashboard displaying grouped task errors

This dashboard interface shows:

  • Failing background task, run/tasks.fragile_task
  • Exception grouped under one issue
  • Six total occurrences
  • The time the error last occurred

This grouping is a game-changer in distributed workloads. A single bug can produce dozens (or even hundreds of retries) across workers. AppSignal spots these patterns right away, so you don’t have to dig through logs to see what’s going on.

Inspecting the Error Details

Click the grouped error to get a detailed view of the failure. You’ll find critical debugging information (normally scattered across worker logs) grouped together.

Here’s what the dashboard shows:

AppSignal error detail page
AppSignal error detail page

You will find:

  • Error message: This is the exception raised during task execution; in this example, the task fails with Simulated failure message.
  • Full traceback: The stack trace shows exactly where the error occurred in the code. Here, the failure originates from tasks.py inside the fragile_task.
  • Task metadata: Each task execution includes a message ID generated by the queue system.
  • Execution environment: The worker hostname (Nehemiah) identifies which worker processed the task.
  • Queue information: The task was executed from the celery queue.
  • Timestamp: The exact time the failure occurred is recorded for every sample.

All this helps you see whether failures were isolated events or if they actually happened repeatedly across multiple workers. This context is very important in distributed task systems. Plus, as mentioned, you don’t have to dig for this information yourself. You get a clear view of which task failed, where it ran, and how often this failure occurred.

AppSignal displaying task attributes
AppSignal displaying task attributes

These attributes provide deeper operational context such as:

  • retry count (celery.retries)
  • worker hostname (celery.hostname)
  • queue routing information
  • task state (FAILURE)

Debugging no longer requires you to work on something vague that failed in the queue. It has become a specific Celery task that failed with this exception across multiple executions.

Why This Matters in Production Systems

Failures are pretty easy to spot in small development environments, but what if the production system runs dozens of Celery workers with multiple processes each?

For example, the worker in this demo runs with 12 concurrency, as seen here:

Celery worker terminal output
Celery worker terminal output

This means up to twelve tasks execute simultaneously. When failures occur under these conditions, logs can grow quickly and conceal the underlying issue.

AppSignal solves this problem by:

  • Capturing exceptions automatically
  • Grouping failures by type
  • Attaching execution metadata
  • Showing trends in the error dashboard

Instead of reading logs line by line, you can immediately identify which task is failing and why.

Patterns to Watch For

Observability is more than just capturing individual failures. As an engineer, you will often encounter patterns that reveal deeper issues in background job systems. Observability tools monitoring dashboards, just like we have seen with AppSignal, make these patterns visible before they become huge production problems.

Retry Storms

A retry helps a task recover from temporary failures, but the downside is they can hide deeper problems. We can equate retries to safety nets of distributed systems designed to handle temporary failures. It is great for improving a system's resilience but can also mask underlying problems.

Often a task retrying several times before succeeding generates multiple failures that might go unnoticed. If you are not monitoring the retry count alongside the success rates, you might miss the fact that tasks are succeeding only after multiple attempts.

The same exception might appear multiple times and then succeed. The retry is not fixing the problem but rather delaying failure. If you see the same exception recurring across multiple retries, then there is a high likelihood you are dealing with a deeper issue that will disrupt the system rather than a random error.

One Task Dragging Down the Queue

Background tasks have distinct behaviors. Some tasks are processed almost immediately, while others take a few seconds. When a slow task occupies a worker process for too long, it reduces the number of workers available for other tasks. This results in slow queue latency, delayed task execution, and reduced system throughput.

External Dependency Failures

There are background tasks that have to interact with some external systems, like APIs, databases, or messaging services. These tasks can appear broken when those dependencies fail, even though they are not the real problem.

Tracebacks can reveal these issues. Some common examples include ConnectionError, TimeoutError, HTTPError. In these cases, the task logic may be correct, but the external service it depends on could be unavailable or the response may be slow.

Alerting on What Matters

Dashboards help when investigating issues, but alerts ensure problems are detected immediately. In background processing systems like Celery, tasks often run silently without direct user interaction. This means failures can continue for hours before anyone notices unless alerting is configured properly.

Effective alerting involves targeting signals that indicate deeper system issues rather than temporary noise. It’s important to always target high-value signals, as they help find the root cause of a problem.

High-value alerts typically include:

Error Rate Spikes

If a task that normally succeeds suddenly begins to fail frequently, it signals a bug, a configuration issue, or a missing dependency. Alerting on unusual error rates helps teams react quickly before failures affect larger workflows.

You can easily spot the tasks that are slowing down the system and fix the issue by going through the data.

New Exception Types

When you spot new exceptions that you haven’t noticed before, new system changes might be at fault. If engineers manage to detect them early enough, they will be able to investigate and fix everything before the issue spreads.

Task Duration Anomalies

A task that normally completes in milliseconds but suddenly starts taking seconds can slow down queue processing. Alerting on performance anomalies helps identify slow queries and overloaded services.

Engineers’ goal is to know when something important breaks. For example, if a task such as send_welcome_email starts failing repeatedly, the team should receive an alert immediately rather than by discovering the issue through user complaints later.

Well-designed alerts focus on high-value signals that help indicate deeper system issues.

Example: Debugging a Silent Failure

Now, let’s consider a background task called process_payment. Its job is to charge a user’s card and update their account once the payment succeeds. Most payments complete successfully, but suddenly there is a small percentage that begins to fail. Since the task is configured to retry, some payments eventually succeed after multiple attempts. From the outside, the system appears healthy.

Without proper visibility, the engineering team will only become aware of this problem when a user submits a support ticket. They will then search through worker logs trying to reconstruct what happened, often without enough context to reproduce the failure.

AppSignal makes investigation easier. The error dashboard quickly reveals a spike in stripe.error.CardError exceptions coming from the process_payment task. Instead of scattered log entries, failures are grouped together under a clean interface.

Looking at the error samples shows additional context:

  • User IDs associated with the failures
  • Timestamps of each occurrence
  • Traceback returned by the Stripe API
  • Number of retries that occur

From there, the pattern becomes clear. Failures occur when a specific card type fails Stripe’s validation rules. Once the pattern is visible, fixing the issue becomes easier.

Wrap-Up

Celery enables workers to run in the background, which represents a big advantage for modern software systems. A task is sent to a broker (RabbitMQ or Redis) and then assigned to workers for processing. Tasks throwing exceptions are retried without affecting the functioning of the whole application. However, these retries can also make it difficult to notice underlying issues that eventually trigger system failures.

In most cases, background tasks fail quietly, especially with no proper visibility. In huge distributed systems, exceptions disappear into worker logs. Over time, it becomes a challenge for teams to discover problems before users report them.

Observability platforms help in addressing this challenge. Instrumenting Celery tasks enables engineering teams to see failures as they happen, track retries, and inspect the full context behind each exception.

If you’re already running Celery in production, the next step is straightforward. Follow the Python installation guide in the AppSignal documentation to add monitoring to your application and start capturing real task behavior.

Wondering what you can do next?

Finished this article? Here are a few more things you can do:

  • Share this article on social media
Nehemiah Bosire

Nehemiah Bosire

A seasoned technical writer, has written content for established industry giants SureSolutions and AvyaTechnologies. Also wrote content for major publications, including Python in Plain English and Stackadem.

All articles by Nehemiah Bosire

Become our next author!

Find out more

AppSignal monitors your apps

AppSignal provides insights for Ruby, Rails, Elixir, Phoenix, Node.js, Express and many other frameworks and libraries. We are located in beautiful Amsterdam. We love stroopwafels. If you do too, let us know. We might send you some!

Discover AppSignal
AppSignal monitors your apps