Anish S
04/28/2026, 8:05 PMError in ASGI application. Do you know why?Anish S
04/28/2026, 8:05 PMMarvin
04/28/2026, 8:06 PMMarvin
04/28/2026, 8:08 PMjson
{"type": "auth", "token": null}
/api/events/in expects:
1. a websocket auth handshake first
2. then actual event payloads
A real event must include fields like:
- occurred
- event
- resource
- id
Your message has none of those, so Pydantic raises:
text
ValidationError: 4 validation errors for Event
Most likely cause
Your client and server are not speaking the websocket protocol the same way. In practice, this usually means one of these:
1. Prefect version mismatch
- I found a recent issue where a 3.6.16 worker was talking to a 3.4.25 server and websocket auth failed:
Workers cannot connect to workpool
2. A reverse proxy / ingress is interfering with websocket upgrade or subprotocol handling
- If the websocket subprotocol/header is stripped, the server can fall back incorrectly and treat the auth message as a normal event payload.
3. A non-Prefect or custom websocket client is sending {"type":"auth","token":null} directly to /api/events/in
- That endpoint is not for arbitrary JSON messages.
What to check first
1. Make sure all Prefect components are on the same 3.x version
- Especially:
- server
- workers
- clients creating deployments/runs
If they differ, upgrade them to the same current release.
2. If you are behind nginx / ingress / load balancer, verify websocket support
- Must preserve:
- Upgrade
- Connection
- websocket traffic to /api/events/in
- If applicable, also ensure websocket subprotocols are preserved.
3. If server auth is enabled, verify the token is actually set
- Check whether PREFECT_SERVER_API_AUTH_STRING is configured.
- If it is configured, a null token will fail auth.
- If it is not configured, null may be acceptable during handshake, but only if the handshake path is handled correctly.
4. Turn on debug logs on the client/worker
- Prefect usually logs websocket auth/connect details more clearly in debug mode:
bash
export PREFECT_DEBUG_MODE=1
export PREFECT_LOGGING_LEVEL=DEBUG
Why this happens internally
The relevant server handler is prefect.server.api.events.stream_events_in, which validates incoming websocket text as an Event. If the auth handshake is not consumed properly first, this line fails:
text
event = Event.model_validate_json(event_json)
So the problem is generally not that your event schema is wrong — it’s that an auth message is reaching the event parser.
Recommended fix
- Upgrade server and workers to the same latest Prefect 3.x version
- Check proxy/ingress websocket handling
- If using custom code, do not send raw auth payloads to /api/events/in unless you’re implementing the full Prefect websocket protocol
Helpful references:
- Prefect Events
- Event specification
- Related GitHub issue
If you want, send me:
- your Prefect server version
- worker version
- whether you’re using nginx/ALB/ingress
- whether PREFECT_SERVER_API_AUTH_STRING is set
and I can help narrow it down quickly.Marvin
04/29/2026, 8:21 PMMarvin
04/29/2026, 8:23 PMPENDING, but the server said “no, this run should stay SCHEDULED instead.”
So in short: the worker did not fail to submit arbitrarily; orchestration rejected the state transition.
Most common reason: concurrency limits
The most common cause is that the run hit a deployment/work-pool/work-queue concurrency rule, so Prefect rescheduled it instead of letting it enter PENDING.
A very typical pattern is:
- worker picks up a run
- worker proposes PENDING
- server sees no concurrency slot is available
- server returns SCHEDULED (often shown in the UI as AwaitingConcurrencySlot)
- worker logs:
text
Aborted submission of flow run '...': Server returned a non-pending state 'SCHEDULED'
This is generally expected behavior, not a bug by itself.
Related issue:
Tasks stuck in AwaitingConcurrencySlot: Server returned a non-pending state 'SCHEDULED'
Other possible causes
- Another worker already grabbed the same run
- Prefect prevents duplicate submission, so a second attempt can get rejected.
- The run was rescheduled by orchestration logic
- For example, because it should not start yet.
- A pause/cancel/orchestration rule intervened
- Less common, but same general mechanism.
What this means operationally
If the run is being rescheduled due to concurrency, the worker is behaving correctly. The run should wait and be retried later.
If it stays stuck in SCHEDULED / AwaitingConcurrencySlot for too long, then check:
- deployment concurrency limits
- work pool / work queue concurrency
- whether another run is holding the slot
- whether a prior run is stuck in RUNNING or otherwise not releasing concurrency
What to inspect
- In the UI, check the run’s current state name
- If it says AwaitingConcurrencySlot, that strongly points to concurrency enforcement.
- Check your deployment settings for concurrency limits.
- Check whether multiple workers are polling the same queue and competing for the same run.
- Check whether an earlier run is stuck and blocking the slot.
Why the wording is confusing
The word Aborted here comes from the worker’s submission step, not necessarily from a system error. It means:
- “I tried to submit this run now”
- “the API responded with a different state”
- “so I won’t launch infrastructure for it”
It does not necessarily mean the flow run is broken.
Relevant docs:
- Work pools
- Deployments
- Flows
If you want, I can help pinpoint it if you share:
- whether the run state in UI says AwaitingConcurrencySlot
- your deployment concurrency limit
- your work pool / queue concurrency settings
- whether you have multiple workers polling the same pool