Colin Buzza
04/01/2026, 7:01 PMMarvin
04/01/2026, 7:01 PMMarvin
04/01/2026, 7:08 PM.fn() works is consistent with bypassing Prefect’s subflow instrumentation (and therefore skipping the code path that builds task_inputs for the API).
- The clean fix is not to rely on .fn() (you lose subflow observability, retries, caching, etc.). First try upgrading to the latest 3.6.x and ensure your client/server are on matching minor versions. If it still reproduces, please share a minimal repro and environment info and we’ll get it triaged.
What I could find
- I did not find an issue that exactly matches “empty arrays in task_inputs → 422” for 3.6.17.
- There is a related 422 “create_task_run” error when running a deployment as a subflow (different validation error, but similar area of the code path): Error running subflow inside another flow. That one was on 3.4.4 and shows that subflow task-run creation is a sensitive area and has seen changes across 3.4–3.6.
Why .fn() “works”
- .fn() calls the underlying Python function directly and skips Prefect’s orchestration for that call. That means no subflow run is created, and no task_inputs are collected/sent — so you avoid the 422 path entirely.
- Trade-off: you lose subflow run tracking, retries, caching, logs tied to a subflow, etc. It’s okay as a temporary workaround, but not ideal long-term.
What likely changed between 3.3.7 and 3.6.x
- There have been multiple changes in 3.4–3.6 around parameter resolution, result tracking, and subflow/parent linking. While I don’t have a single PR to point at for your exact symptom, it’s plausible a regression in how subflow parameters are collected/serialized led to your task_inputs being built incorrectly (showing up empty).
Recommended next steps
1) Confirm version alignment
- Are you on Prefect Cloud or self-hosted Server?
- If self-hosted, what’s the API version? Please share the output of:
prefect version
from:
- the environment running the parent flow (client)
- the Prefect server (API)
- Prefect recommends keeping client and server on the same minor version. A mismatch can produce 422s if the schema changed.
2) Try the latest 3.6.x
- If you can, upgrade to the latest 3.6.x (there have been a lot of patch fixes). If the issue persists there, we’ll treat it as a current bug.
3) Minimal repro
- If possible, try this minimal example in your environment and share whether it reproduces:
from prefect import flow
@flow
def child(param: str | None):
return param
@flow
def parent():
# This should create a subflow run
return child(param="value")
if __name__ == "__main__":
parent()
- If it fails, please share:
- Any debug logs around the POST to /api/task_runs/
- A redacted snippet of the request body that shows task_inputs as empty arrays
4) Interim workarounds (if you must stay on 3.6.17)
- .fn() is acceptable as a stopgap if you can accept the loss of subflow orchestration:
child.fn(param="value")
- Alternatively, if you don’t need a true subflow, consider making the “child” a task inside the parent flow.
If you can share the version info and a minimal repro, I can open a bug report with the Prefect team (or you can, if you prefer). I’ll also keep an eye out for any related fixes in the next patch releases.