Skip to content

Document favorite_dag's 409 and return a readable conflict message - #73411

Open
vsima wants to merge 2 commits into
apache:mainfrom
vsima:fix-favorite-dag-409
Open

vsima wants to merge 2 commits into
apache:mainfrom
vsima:fix-favorite-dag-409

Conversation

@vsima

@vsima vsima commented Sep 20, 2026

Copy link
Copy Markdown

Document the 409 on POST /dags/{dag_id}/favorite, and make its body match its sibling.

Two related problems on the same endpoint pair.

The published spec contradicts the project's own test. favorite_dag
declares create_openapi_http_exception_doc([404]) while unfavorite_dag
declares [404, 409]. Favoriting an already-favorited Dag does return 409 —
test_favoriting_already_favorited_dag_returns_409 has asserted exactly that
since the endpoint was added in #51264. Because the response was never declared, it is
absent from v2-rest-api-generated.yaml, and so from every client generated
from it: a generated client sees an undeclared status and surfaces it as an
unexpected error rather than "already a favorite".

The two endpoints answer the same conflict differently. unfavorite_dag
checks for the row and raises a 409 with Dag is not marked as favorite.
favorite_dag has no check, so the duplicate insert violates the unique
constraint and is caught by the generic _UniqueConstraintErrorHandler. The
body a user gets for favoriting twice is:

{
  "message": "Serious error when handling your request. Check logs for more details - you will find it in api server when you look for ID 6Q4Msafe",
  "orig_error": "hidden",
  "reason": "Unique constraint violation",
  "statement": "hidden"
}

Favoriting something that is already a favorite is a stale toggle (a second
tab, a cached list, an API client retrying), not a serious error, and it does not warrant sending an operator into the API
server logs after a correlation ID. The same user error produces a clean,
translatable message in one direction and this in the other.

This adds the existence check to favorite_dag, mirroring unfavorite_dag
twenty lines below, and declares the 409.

Behaviour change, stated plainly: the status code does not change. The
409's detail changes from the handler's object above (which also carries the
SQL statement and dialect error when [api] expose_stacktrace is on) to the
string Dag is already marked as favorite, the same shape unfavorite_dag
returns. A repeat favorite also stops writing an ERROR-level line to the API
server log. Anything matching on the old payload would be affected; the only
in-tree references to Unique constraint violation are the handler's own tests
in test_exceptions.py, which do not touch this endpoint, and the UI does not
branch on either message.

On the race: the check is not atomic, so two concurrent favorite calls can
still reach the unique constraint and return the handler's 409. That is the
same window unfavorite_dag has, and the status is unchanged either way, so
this keeps the existing pattern rather than introducing a different one here.
Happy to take the ON CONFLICT DO NOTHING route instead if reviewers prefer.

Tests: test_favoriting_already_favorited_dag_returns_409 now asserts the
body as well as the status. The new probe gets
test_favorite_dag_existence_check_is_bounded, the twin of the existing
unfavorite test, so both existence checks are held to LIMIT 1.

Verified locally: pytest tests/unit/api_fastapi/core_api/routes/public/test_dags.py -k favorite
gives 17 passed. Reverting the route change alone makes
test_favoriting_already_favorited_dag_returns_409 fail on the body, which is
where the payload above came from, so the assertion is pinned to the fix rather
than passing incidentally.

The spec and the UI client (types.gen.ts, services.gen.ts) are the output
of run_generate_openapi_spec.py and pnpm codegen; nothing else in the
generated files moved.


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Opus 5, Fable 5.1)

Generated-by: Claude Code (Opus 5, Fable 5.1) following the guidelines

favorite_dag returns 409 when the Dag is already a favorite, and its test
has asserted that since apache#51264, but the response was never declared, so it
is missing from the OpenAPI spec and every client generated from it.

The 409 also came from the generic unique-constraint handler, so a repeat
favorite answered "Serious error when handling your request" and logged an
error, while unfavorite_dag answers "Dag is not marked as favorite". Check
for the row first, as unfavorite_dag does, and declare the 409.
@boring-cyborg

boring-cyborg Bot commented Sep 20, 2026

Copy link
Copy Markdown

Congratulations on your first Pull Request and welcome to the Apache Airflow community! If you have any issues or are unsure about any anything please check our Contributors' Guide
Here are some useful points:

  • Pay attention to the quality of your code (ruff, mypy and type annotations). Our prek-hooks will help you with that.
  • In case of a new feature add useful documentation (in docstrings or in docs/ directory). Adding a new operator? Check this short guide Consider adding an example Dag that shows how users should use it.
  • Consider using Breeze environment for testing locally, it's a heavy docker but it ships with a working Airflow and a lot of integrations.
  • Be patient and persistent. It might take some time to get a review or get the final approval from Committers.
  • Please follow ASF Code of Conduct for all communication including (but not limited to) comments on Pull Requests, Mailing list and Slack.
  • Be sure to read the Airflow Coding style.
  • Always keep your Pull Requests rebased, otherwise your build might fail due to changes not related to your commits.
    Apache Airflow is a community-driven project and together we are making it better 🚀.
    In case of doubts contact the developers at:
    Mailing List: dev@airflow.apache.org
    Slack: https://s.apache.org/airflow-slack

@vsima
vsima marked this pull request as draft September 20, 2026 15:49
It pinned behaviour this change does not touch, which the review
guidelines count as padding rather than coverage.
@vsima vsima changed the title Document the 409 on favorite_dag and return a readable conflict message Document favorite_dag's 409 and return a readable conflict message Sep 20, 2026
@vsima
vsima marked this pull request as ready for review September 20, 2026 17:10
@bbovenzi bbovenzi added this to the Airflow 3.3.3 milestone Sep 21, 2026
@vsima

vsima commented Sep 22, 2026

Copy link
Copy Markdown
Author

Thanks for the review. The two failures are infra: an artifact-upload 403 in Latest Boto (its tests passed) and a connection reset in the FAB docs spellcheck. Would someone mind re-running those two jobs?

@Andrushika

Andrushika commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

As a non-committer, we can trigger CI ourselves -- simply turn it into draft PR and reopen it again.
(Just a little tip for u)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:API Airflow's REST/HTTP API area:UI Related to UI/UX. For Frontend Developers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants