You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While helping someone troubleshoot a CI failure on one of their PRs, I think I may have found an issue with how action_logging works in the FastAPI routes. The API is definitely one of my blind spots so I could be entirely mistaken about this, and I passed my notes and thoughts into Cline/Claude to hep consolidate them into something that hopefully makes sense... so apologies for the blatant LLM formatting but I figure it was better than a bullet list of half-formed thoughts and observations.
I'm hoping someone with real API experience can either verify this is an actual issue or close this. (@pierrejeambrun, @bugraoz93 ??)
The action_logging() decorator is registered as a FastAPI Depends() on several routes (variables, connections, pools, etc.). Because FastAPI evaluates dependencies before executing the route function body, the audit log entry is written and committed before the route has a chance to validate the request.
This means that requests which will be rejected (providing team_name when multi_team is disabled was the example that led to this, for example) still produce an audit log entry with session.commit(). This has two consequences:
Incorrect audit trail — rejected/invalid requests appear in the audit log as if they were attempted actions
SQLite lock conflicts in tests — the eager session.commit() in the logging dependency can collide with other sessions that hold write locks (I think this was the root cause of a database is locked test failure in Feat/check multi team enabled when team name provided api #63994)
Where the problem lives:
airflow-core/src/airflow/api_fastapi/logging/decorators.py — the log_action inner function (returned by action_logging()) creates a Log object and calls session.commit() unconditionally at the end
All routes that use dependencies=[..., Depends(action_logging()), ...] are affected
Affected routes (non-exhaustive):
POST /variables and PATCH /variables/{key}
POST /connections and PATCH /connections/{connection_id}
POST /pools and PATCH /pools/{pool_name}
(and others — search for Depends(action_logging()))
Claude's suggested fix (someone with better API background needs to confirm this is a good idea first):
Move request validation checks (like the multi_team check) into their own FastAPI dependency that runs beforeaction_logging(). This way invalid requests are rejected before any audit log entry is created. For example:
# Current (validation happens in route body, AFTER action_logging fires):@router.post("",dependencies=[Depends(action_logging()), Depends(requires_access_variable("POST"))],)defpost_variable(post_body: ..., session: SessionDep):
ifpost_body.team_nameisnotNoneandnotconf.getboolean("core", "multi_team"):
raiseHTTPException(400, "team_name cannot be set when multi_team mode is disabled.")
...
# Proposed (validation runs as a dependency, BEFORE action_logging):defvalidate_team_name_allowed(request: Request):
"""Reject requests that include team_name when multi_team is disabled."""# Only check for methods that accept a bodyifrequest.methodin ("POST", "PATCH", "PUT"):
body=awaitrequest.json()
ifbody.get("team_name") isnotNoneandnotconf.getboolean("core", "multi_team"):
raiseHTTPException(400, "team_name cannot be set when multi_team mode is disabled.")
@router.post("",dependencies=[Depends(validate_team_name_allowed), # runs firstDepends(action_logging()),Depends(requires_access_variable("POST")), ],)defpost_variable(post_body: ..., session: SessionDep):
...
Alternatively, the action_logging decorator itself could be made conditional — only commit the log if the route succeeds — but that's a bigger change with wider implications for the audit trail design.
Apache Airflow version
main
What happened and how to reproduce it?
While helping someone troubleshoot a CI failure on one of their PRs, I think I may have found an issue with how
action_loggingworks in the FastAPI routes. The API is definitely one of my blind spots so I could be entirely mistaken about this, and I passed my notes and thoughts into Cline/Claude to hep consolidate them into something that hopefully makes sense... so apologies for the blatant LLM formatting but I figure it was better than a bullet list of half-formed thoughts and observations.I'm hoping someone with real API experience can either verify this is an actual issue or close this. (@pierrejeambrun, @bugraoz93 ??)
The
action_logging()decorator is registered as a FastAPIDepends()on several routes (variables, connections, pools, etc.). Because FastAPI evaluates dependencies before executing the route function body, the audit log entry is written and committed before the route has a chance to validate the request.This means that requests which will be rejected (providing
team_namewhenmulti_teamis disabled was the example that led to this, for example) still produce an audit log entry withsession.commit(). This has two consequences:session.commit()in the logging dependency can collide with other sessions that hold write locks (I think this was the root cause of adatabase is lockedtest failure in Feat/check multi team enabled when team name provided api #63994)Where the problem lives:
airflow-core/src/airflow/api_fastapi/logging/decorators.py— thelog_actioninner function (returned byaction_logging()) creates aLogobject and callssession.commit()unconditionally at the enddependencies=[..., Depends(action_logging()), ...]are affectedAffected routes (non-exhaustive):
POST /variablesandPATCH /variables/{key}POST /connectionsandPATCH /connections/{connection_id}POST /poolsandPATCH /pools/{pool_name}Depends(action_logging()))Claude's suggested fix (someone with better API background needs to confirm this is a good idea first):
Move request validation checks (like the
multi_teamcheck) into their own FastAPI dependency that runs beforeaction_logging(). This way invalid requests are rejected before any audit log entry is created. For example:Alternatively, the
action_loggingdecorator itself could be made conditional — only commit the log if the route succeeds — but that's a bigger change with wider implications for the audit trail design.What you think should happen instead?
No response
Operating System
No response
Versions of Apache Airflow Providers
No response
Deployment
None
Deployment details
No response
Anything else?
No response
Are you willing to submit PR?
Code of Conduct