apache / apache/airflow

Airflowctl dags trigger fails with extra_forbidden against Airflow < 3.2.0

Open Beginner friendly
#70,327 1 comment 0 reactions 0 assignees View on GitHub
area:airflow-ctl kind:bug needs-triage
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.