The GitHub Actions job "Tests (ARM)" on airflow.git/main has failed.
Run started by GitHub user potiuk (triggered by potiuk).

Head commit for run:
ab2851bc7452f34caad35de6b68c082725b53225 / Stefan Wang <[email protected]>
Return a 422 when the database rejects an API payload (#66888)

* Translate DataError to 413/422 instead of generic 500

Triggering a DAG run with an oversized 'conf' payload (and other
DB-rejected writes across the API surface) currently produces a generic
500. The SQL error surfaces deep in SQLAlchemy as
(1406, "Data too long for column 'conf' at row 1") on MySQL, the
caller has no signal that payload size was the cause, and every write
endpoint that touches a length-capped column has the same shape today
(Connection.extra, Variable.val, XCom.value, TaskInstance.note, HITL
fields, etc).

Add a single FastAPI exception handler for sqlalchemy.exc.DataError on
both the public REST API and the execution API. 'Data too long' /
'too large' / 'too big' errors map to 413 Content Too Large; other
DataErrors (out-of-range, numeric overflow) map to 422. The response
body carries the original DB error and an actionable hint pointing at
either reducing the payload or widening the column type on MySQL.

Every existing and future write endpoint inherits the translation
automatically. Postgres deployments never hit it (JSONB has no length
cap); MySQL deployments get a clear 4xx + remediation hint instead
of a generic 500.

Closes #66779

Signed-off-by: 1fanwang <[email protected]>

* Rename newsfragment to PR number

Signed-off-by: 1fanwang <[email protected]>

* Fix mypy errors on execution_api handler registration + test indexing

Pass DataError directly to add_exception_handler instead of via the
BaseErrorHandler.exception_cls attribute (typed as instance T, not
type[T]) so the call type-checks against Starlette's expected
type[Exception]. The variance issue between Callable[Request, DataError]
and Callable[Request, Exception] is silenced with a type-ignore matching
the existing pattern used in the core_api ERROR_HANDLERS loop.

In the new TestDataErrorHandler tests, extract HTTPException.detail
into a typed dict before subscripting so mypy stops inferring it as str.

Signed-off-by: 1fanwang <[email protected]>

* Simplify DataError handling per review

Drop the 413/'too long' marker special-case and always return 422 to
match the rest of the app. Add `statement` to the response detail so
the shape mirrors the unique-constraint handler. Drop the manual
handler block in the execution API since `init_error_handlers` already
loops over `ERROR_HANDLERS` for both apps. Rename `_DataErrorHandler`
to `DataErrorHandler`. Remove the newsfragment.

* Hide DataError API response internals behind expose_stacktrace

The DataError handler now mirrors _UniqueConstraintErrorHandler: it logs
the statement and underlying database error under a lookup id and only
echoes them back to the caller when [api] expose_stacktrace is set. By
default the response carries an opaque message plus the id, so column
names and rejected values are no longer leaked to API clients.

Tests cover both expose_stacktrace False (internals hidden) and True
(internals exposed) across the dialect error shapes.

* Stop task-instance execution routes from masking DataError

ti_run and ti_update_state wrap their DB writes in catches broader than
DataError: SQLAlchemyError becomes an opaque 500, and a bare Exception in
ti_update_state silently marks the task FAILED and returns 204. Those fire
before the app-level DataErrorHandler can translate the rejection, so an
oversized field (note, rendered_map_index, next_kwargs, ...) either returns
a generic 500 or leaves the worker believing the update succeeded while the
server marked the task FAILED.

Re-raise DataError ahead of each broad catch so it reaches the handler and
the caller gets the actionable 422. The fallbacks for other unexpected
errors are unchanged.

* Tighten DataError docstring and re-raise comments

Drop the per-dialect enumeration in the handler docstring and collapse the
multi-line re-raise comments to one line each.

* Test DataError handling in the task-instance execution routes

Add MySQL/Postgres-only route tests (SQLite ignores varchar limits): an
oversized field the database rejects returns 422 from ti_run and
ti_update_state, and the deferred-state path no longer silently marks the
TI FAILED.

---------

Signed-off-by: 1fanwang <[email protected]>

Report URL: https://github.com/apache/airflow/actions/runs/27445581203

With regards,
GitHub Actions via GitBox


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to