How to Fix 'callback not firing in production' in CrewAI (Python)

By Cyprian AaronsUpdated 2026-04-21
callback-not-firing-in-productioncrewaipython

What this error usually means

If your CrewAI callback works locally but never fires in production, the issue is usually not CrewAI itself. It means the callback was registered in a way that only works for one execution path, or the process running in production never reaches the event where the callback is invoked.

In practice, this shows up when you move from a local script to Docker, FastAPI, Celery, serverless, or a multi-process setup and suddenly your Task or Crew callback stops being called.

The Most Common Cause

The #1 cause is passing the callback in the wrong place or relying on an object that gets recreated in production.

In CrewAI, callbacks are typically attached to Task, Agent, or Crew depending on what event you want to capture. A common mistake is defining a function correctly but never wiring it to the actual Task that runs, or creating the callback inside a request scope that disappears before execution finishes.

Broken vs fixed pattern

Broken patternFixed pattern
Callback defined, but not attached to the executed taskCallback attached directly to the task that runs
Callback object recreated per requestStable callback instance passed into crew/task construction
Local sync script works, async/prod worker loses referenceExplicit wiring at startup
# BROKEN
from crewai import Agent, Task, Crew

def on_complete(output):
    print("Task completed:", output)

agent = Agent(
    role="Researcher",
    goal="Research company info",
    backstory="You research facts."
)

task = Task(
    description="Summarize ACME Corp",
    agent=agent,
)

crew = Crew(
    agents=[agent],
    tasks=[task],
)

result = crew.kickoff()
# on_complete never fires because it was never attached anywhere
# FIXED
from crewai import Agent, Task, Crew

def on_complete(output):
    print("Task completed:", output)

agent = Agent(
    role="Researcher",
    goal="Research company info",
    backstory="You research facts."
)

task = Task(
    description="Summarize ACME Corp",
    agent=agent,
    callback=on_complete,   # attach it here
)

crew = Crew(
    agents=[agent],
    tasks=[task],
)

result = crew.kickoff()

If you’re using a crew-level callback pattern in your version of CrewAI, make sure you’re using the exact supported signature for that version. A mismatch between your installed package and the docs can produce behavior like:

  • no callback execution
  • silent failures
  • TypeError: on_complete() takes 1 positional argument but 2 were given

Other Possible Causes

1) Production is running a different CrewAI version

This is common when local pip install crewai differs from your Docker image or deployed lockfile.

Local:  crewai==0.80.x
Prod:   crewai==0.70.x

A version mismatch can change:

  • callback parameter names
  • whether callbacks are supported on Task
  • whether callbacks are sync-only

Check with:

pip show crewai
pip freeze | grep crewai

2) Your process exits before the callback runs

This happens in short-lived jobs and serverless handlers.

from crewai import Crew

result = crew.kickoff()
return {"status": "started"}  # process/request ends too early in some deployments

If your execution model is asynchronous or backgrounded, make sure you await completion or keep the worker alive until kickoff returns.

3) Exceptions inside the callback are swallowed by logging

Your callback may fire and fail immediately.

def on_complete(output):
    raise RuntimeError("callback broke")

In production, this can look like “callback not firing” because you only see upstream logs. Wrap it:

def on_complete(output):
    try:
        print(output)
        # send metrics / webhook / DB write
    except Exception as e:
        logger.exception("callback failed: %s", e)

4) Multiprocessing / Celery / Gunicorn worker isolation

If you register callbacks in one process and execute CrewAI in another, the function reference may not survive serialization.

Bad pattern:

# created in web process, executed in worker process
task = Task(description="...", agent=agent, callback=on_complete)

If on_complete closes over non-picklable objects like DB sessions or request context, workers may drop it or fail silently. Use module-level functions and pass primitive data only.

5) You attached the callback to the wrong object

CrewAI has multiple layers:

  • Agent
  • Task
  • Crew

If you expect task completion output but attach a crew-level handler meant for another event type, nothing useful happens.

Example:

crew = Crew(
    agents=[agent],
    tasks=[task],
    # wrong place for this specific event in your version/setup
)

Check which lifecycle hook your installed version actually supports.

How to Debug It

  1. Print every lifecycle boundary Add logs before kickoff, inside the task definition path, and inside the callback. If you don’t see the “before kickoff” log in prod, your code path isn’t even reaching CrewAI.

  2. Verify exact installed versions Run this in production startup logs:

    import crewai
    print(crewai.__version__)
    

    Compare it with local. If they differ, fix your lockfile and rebuild.

  3. Reduce to a module-level sync example Remove FastAPI/Celery/background workers and run a plain Python script. If it works there but not in prod, the problem is process lifecycle or serialization.

  4. Make the callback fail loudly Put explicit logging at entry:

    def on_complete(output):
        print("CALLBACK HIT")
        print(type(output), output)
    

    If even this doesn’t show up, the callback isn’t wired correctly or isn’t surviving deployment boundaries.

Prevention

  • Define callbacks as module-level functions, not nested closures inside request handlers.
  • Pin CrewAI versions with a lockfile and test deployment images against local installs.
  • Keep callbacks simple: log first, then enqueue work to another system if needed.
  • Add an integration test that runs crew.kickoff() and asserts callback side effects happened.

If you’re seeing callback not firing in production, start by checking wiring first, then runtime model second. In most cases it’s not a CrewAI bug; it’s a mismatch between how Python objects behave locally and how your production worker actually executes them.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides