Airflowctl dags trigger fails with extra_forbidden against Airflow < 3.2.0
- Dominant language
- Python
- Stars
- 46.9k
- Forks
- 17.8k
- Avg merge
- 2d 9h
- Merged PRs (30d)
- 472
Description
### Airflow CTL Version
Latest stable version
### Airflow CTL Command
airflowctl dags trigger example_simplest_dag --dag-run-id --env production
### Keyring Backend / Version
Keyring Backend/Headless
### Auth Type
Token
### What is the current behaviour?
`airflowctl dags trigger` fails against any Airflow deployment running a version earlier than 3.2.0, with:
{'type': 'extra_forbidden', 'loc': ['body', 'partition_key'], 'msg': 'Extra inputs are not permitted', 'input': None}
Root cause: `DagOperations.trigger()` in `airflow-ctl/src/airflowctl/api/operations.py` serializes the request body without `exclude_none=True`:
```python
json=trigger_dag_run.model_dump(mode="json")
```
`TriggerDAGRunPostBody` includes `partition_key` and `bundle_version`, both added in Airflow 3.2.0's model. Since these default to `None` and are never excluded from serialization, every trigger request includes them as `null` regardless of the target server's Airflow version. Any server running < 3.2.0 doesn't recognize these fields and rejects the request entirely.
`AssetsOperations.create_event()` in the same file already handles this correctly:
```python
json=asset_event_body.model_dump(mode="json", exclude_none=True)
```
Confirmed via the live OpenAPI schema on a 3.1.x deployment (`curl .../openapi.json`) that `partition_key`/`bundle_version` genuinely don't exist in that server's `TriggerDAGRunPostBody` schema, and confirmed directly against the `apache/airflow` source that these fields were added starting at the `3.2.0` tag (absent in `3.1.0` through `3.1.8`).
### What is the expected results?
`airflowctl dags trigger` should succeed against any Airflow API server version whose `TriggerDAGRunPostBody` schema it's compatible with, i.e., it shouldn't send fields the target server doesn't recognize just because the client's own model happens to declare them, when those fields were never explicitly set by the caller.
### Anything else?
Steps to reproduce:
1. Deploy Airflow 3.1.x (reproduced on 3.1.0 via Charmed Airflow on Kubernetes)
2. `airflowctl auth login --api-url --env --skip-keyring`
3. `airflowctl dags trigger --dag-run-id --env `
4. Observe the `extra_forbidden` error
Fix confirmed locally: adding `exclude_none=True` to `trigger()`'s `model_dump()` call resolves the issue, tested both with and without `--logical-date` passed explicitly. PR incoming with the fix plus a regression test.
### Are you willing to submit PR?
- [x] Yes I am willing to submit a PR!
### Code of Conduct
- [x] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)
Contributor guide
Research direction
Start in airflow-ctl/src/airflowctl/api/operations.py at DagOperations.trigger(), then compare its request serialization with AssetsOperations.create_event(). Reproduce against an Airflow 3.1.x server, add the regression coverage described in the issue, and verify triggering works with and without --logical-date while omitted optional fields are not sent.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, cli
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100