Roman Bedroš
04/24/2026, 9:52 AMMarvin
04/24/2026, 9:52 AMMarvin
04/24/2026, 9:56 AMRuntimeError: can't register atexit after shutdown is a Python error. It happens when code tries to set up cleanup hooks after Python has already started exiting. In Prefect, this can happen because some internal services/threads are initialized lazily, and if they get touched during interpreter shutdown, Python rejects the atexit.register(...) call.
So in practice, this usually means:
- the flow container/process is already shutting down
- something in Prefect gets initialized very late during shutdown
- Python raises this error
What it usually does not mean
- not usually “the database is corrupted”
- not usually “Prefect Server is dead”
- not usually the root cause of a long-term server outage
But it can coincide with:
- a stuck flow runner shutdown
- delayed container termination
- temporary UI weirdness if the server is waiting on API/db work or the environment is resource constrained
Most likely causes in Docker setups
Given your setup:
- one container = Prefect server/UI/API
- another container = script that runs flows
common triggers are:
1. The flow container exits immediately after running work
- Prefect still tries to flush logs/events/telemetry during shutdown
2. Container stop timeout is too short
- Docker sends SIGTERM, then later SIGKILL
- the process gets caught in partial shutdown for tens of seconds
3. Python version issue
- Python `3.12.0`–`3.12.2` had shutdown/finalization issues that made these situations worse
4. Resource pressure
- if CPU / memory / disk is tight, both the server and runner can look “hung”
- the UI spinning for ~1 minute can also be slow API responses or DB lock/contention, not just this error
What I’d try first
1. Disable telemetry in the runner container
Set:
DO_NOT_TRACK=1
This can reduce shutdown-time background activity.
2. Increase Docker stop grace period
In docker-compose.yml for the flow container:
stop_grace_period: 90s
3. Check Python version
If you are on Python `3.12.0`–`3.12.2`, upgrade to 3.12.3+ or use 3.11.
4. Turn on Prefect debug logging
In the flow container:
PREFECT_LOGGING_LEVEL=DEBUG
PREFECT_LOGGING_INTERNAL_LEVEL=DEBUG
5. Check whether the server container is actually blocked, or just the runner is noisy
Look at timestamps in both containers during the minute when UI refresh hangs.
What to inspect right now
In the runner container logs, search for:
atexit
RuntimeError
shutdown
Completed flow run
In the server container logs, search around the same timestamp for:
ERROR
WARNING
timeout
database
sqlalchemy
Also check whether the server container is CPU or memory starved:
docker stats
and whether the runner is being killed:
docker ps -a
docker inspect <runner-container>
If exit code is:
- 0 → probably harmless shutdown noise
- 137 → container was killed, often timeout / OOM / SIGKILL
- 1 → process failed during shutdown
Important nuance
If your “script with flows” is just a plain Python script that starts flows and then exits, this shutdown message is more plausible. If instead you're running a worker or a long-lived service, then I’d want to inspect how that process is being started/stopped.
Recommendation
I’d treat this as:
- possibly a symptom, not yet proven as the root cause of the UI hang
The better question is:
when the UI spins for a minute, what do the server logs show at that exact time?
If you want, send:
- your docker-compose.yml (or container run commands)
- Python version
- how you start the flow container
- 30–50 lines of logs from both containers around the timestamp of the hang
and I can help narrow down whether this is:
- harmless shutdown noise,Marvin
04/24/2026, 9:56 AM