Fix DatabaseError: django_celery_beat.models.PeriodicTask.DoesNotExist / celery.beat.SchedulingError: schedule not found in Celery
This error occurs when Celery Beat cannot load or parse the periodic task schedule. It often happens when the schedule configuration references a task that does not exist, the schedule dict has invalid crontab syntax, or the Beat database is corrupted. Fix it by verifying task names match registered tasks, checking crontab syntax, and resetting the Beat schedule file if needed.
Reading the Stack Trace
Here's what each line means:
- File "/app/venv/lib/python3.12/site-packages/celery/beat.py", line 345, in apply_async: Celery Beat tries to apply the scheduled task but cannot find or parse the schedule entry.
- File "/app/venv/lib/python3.12/site-packages/celery/schedules.py", line 482, in remaining_estimate: The crontab parser found an invalid value (hour=25 is outside the valid 0-23 range).
- celery.beat.SchedulingError: Received invalid schedule entry: crontab(minute='*/5', hour='25'): The schedule entry has hour=25 which is not a valid hour. Valid values are 0-23.
Common Causes
1. Invalid crontab syntax
The schedule uses an out-of-range value or invalid crontab expression.
app.conf.beat_schedule = {
'cleanup-every-night': {
'task': 'app.tasks.cleanup',
'schedule': crontab(minute='*/5', hour='25'), # hour 25 invalid
}
}
2. Task name does not match registered task
The schedule references a task path that does not match any task registered with Celery.
app.conf.beat_schedule = {
'send-digest': {
'task': 'app.tasks.send_digest', # actual task is 'app.tasks.email.send_digest'
'schedule': crontab(hour=8, minute=0),
}
}
3. Corrupted Beat schedule database
The celerybeat-schedule.db file is corrupted, causing Beat to fail on startup.
# celerybeat-schedule.db file is corrupted
# celery -A app beat --loglevel=info
# ERROR: Unrecoverable error in scheduler
The Fix
Fix the invalid crontab value (hour must be 0-23) and ensure task names exactly match the dotted path of registered Celery tasks. If the Beat schedule database is corrupted, delete celerybeat-schedule.db and restart Beat to regenerate it.
from celery.schedules import crontab
app.conf.beat_schedule = {
'cleanup-every-night': {
'task': 'app.tasks.cleanup',
'schedule': crontab(minute='*/5', hour='25'),
}
}
from celery.schedules import crontab
app.conf.beat_schedule = {
'cleanup-every-night': {
'task': 'app.tasks.cleanup',
'schedule': crontab(minute=0, hour=2), # 2:00 AM daily
},
'send-digest': {
'task': 'app.tasks.email.send_digest', # matches registered task path
'schedule': crontab(hour=8, minute=0),
}
}
Testing the Fix
import pytest
from celery.schedules import crontab
from app import create_app, celery
@pytest.fixture
def app():
app = create_app()
app.config['TESTING'] = True
return app
def test_beat_schedule_has_valid_entries(app):
schedule = celery.conf.beat_schedule
assert len(schedule) > 0
for name, entry in schedule.items():
assert 'task' in entry
assert 'schedule' in entry
def test_crontab_values_are_valid():
# Should not raise
crontab(minute=0, hour=2)
crontab(hour=8, minute=0)
crontab(minute='*/15')
def test_invalid_crontab_raises():
with pytest.raises(Exception):
# hour=25 should be rejected
c = crontab(minute='*/5', hour='25')
c.remaining_estimate(c.now())
def test_all_scheduled_tasks_are_registered(app):
with app.app_context():
registered = celery.tasks.keys()
for name, entry in celery.conf.beat_schedule.items():
assert entry['task'] in registered, f"Task {entry['task']} not registered"
Run your tests:
pytest tests/ -v
Pushing Through CI/CD
git checkout -b fix/celery-beat-schedule,git add celeryconfig.py app/tasks.py,git commit -m "fix: correct invalid crontab syntax and task names in beat schedule",git push origin fix/celery-beat-schedule
Your CI config should look something like this:
name: CI
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
redis:
image: redis:7-alpine
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pytest tests/ -v --tb=short
env:
CELERY_BROKER_URL: redis://localhost:6379/0
The Full Manual Process: 18 Steps
Here's every step you just went through to fix this one bug:
- Notice the error alert or see it in your monitoring tool
- Open the error dashboard and read the stack trace
- Identify the file and line number from the stack trace
- Open your IDE and navigate to the file
- Read the surrounding code to understand context
- Reproduce the error locally
- Identify the root cause
- Write the fix
- Run the test suite locally
- Fix any failing tests
- Write new tests covering the edge case
- Run the full test suite again
- Create a new git branch
- Commit and push your changes
- Open a pull request
- Wait for code review
- Merge and deploy to production
- Monitor production to confirm the error is resolved
Total time: 30-60 minutes. For one bug.
Or Let bugstack Fix It in Under 2 minutes
Every step above? bugstack does it automatically.
Step 1: Install the SDK
pip install bugstack
Step 2: Initialize
import bugstack
bugstack.init(api_key=os.environ["BUGSTACK_API_KEY"])
Step 3: There is no step 3.
bugstack handles everything from here:
- Captures the stack trace and request context
- Pulls the relevant source files from your GitHub repo
- Analyzes the error and understands the code context
- Generates a minimal, verified fix
- Runs your existing test suite
- Pushes through your CI/CD pipeline
- Deploys to production (or opens a PR for review)
Time from error to fix deployed: Under 2 minutes.
Human involvement: zero.
Try bugstack Free →No credit card. 5-minute setup. Cancel anytime.
Deploying the Fix (Manual Path)
- Run pytest locally to confirm all schedule entries are valid.
- Open a pull request with the schedule fix.
- Wait for CI checks to pass on the PR.
- Have a teammate review and approve the PR.
- Merge to main, delete the old celerybeat-schedule.db, and restart Beat in staging.
Frequently Asked Questions
BugStack validates all crontab expressions, confirms every scheduled task name maps to a registered task, and runs your full suite before marking it safe.
BugStack never pushes directly to production. Every fix goes through a pull request with full CI checks, so your team can review it before merging.
For most apps, the config file is simpler and version-controlled. Use django-celery-beat or a database-backed scheduler if you need dynamic schedule changes at runtime.
Run celery -A app inspect registered to see all tasks known to the running workers. Compare this list against your beat_schedule entries.