Skip to content

Fix airflowctl tracebacks on an unusable credentials file - #72468

Closed
Eason09053360 wants to merge 1 commit into
apache:mainfrom
Eason09053360:fix-airflowctl-unreadable-credentials-traceback
Closed

Eason09053360 wants to merge 1 commit into
apache:mainfrom
Eason09053360:fix-airflowctl-unreadable-credentials-traceback

Conversation

@Eason09053360

@Eason09053360 Eason09053360 commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Why

Credentials.load() guarded only against the credentials file being absent. Every other way that file can be unusable reached the operator as a stack trace, because safe_call_command recognises none of those exception types:

file state before
wrong permissions PermissionError
non-UTF-8 bytes UnicodeDecodeError
truncated or empty JSONDecodeError
valid JSON, wrong shape TypeError
no api_url key KeyError: 'api_url'

$AIRFLOW_HOME/production.json is a generic name in a shared directory, so a foreign or half-written file is not far-fetched. The traceback was also a dead end: the fix is to log in again, but auth login loads the same file and failed the same way, leaving deleting the file by hand as the only way out.

What

_read_cli_config(path) in airflow-ctl/src/airflowctl/api/client.py takes over the open and the parse, which is what puts all of those cases in one place — OSError for unreadable paths, ValueError for both malformed JSON and non-UTF-8 bytes (siblings, not parent and child), plus an explicit check that the parsed result is a JSON object with an api_url. They raise AirflowCtlCredentialInvalidException, a new AirflowCtlException subclass. FileNotFoundError is re-raised untouched so callers keep handling an absent file as the distinct case it is, and the debug-credentials read goes through the same helper.

load() extends the tolerance it already had for a missing file to an unusable one, so auth login — the recovery the message recommends — can run and replace it.

safe_call_command catches AirflowCtlException rather than a hand-maintained tuple of subclasses. The tuple was already equivalent to the base class, and keeping it manual means the next exception class added anywhere regresses to a traceback, which is the failure this PR is fixing. One side effect: the path-traversal guard in _safe_path_under_airflow_home raises AirflowCtlException directly and so was not caught before; it now renders as a message.

Tests cover each of the file states above and that AUTH / NO_AUTH clients still load past an unusable file.

Deliberately out of scope: {"api_url": null} still loads as None and lets get_client fall back to localhost:8080. That is a silently-wrong-server bug rather than a traceback, Credentials.save() itself writes such a file, and an existing test pins the current behaviour — it wants its own change. auth list-envs also reads the same file through its own logic, which now defines "unusable" slightly differently.


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

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

Reading the credentials file guarded only against the file being absent, so
anything else about it that stopped a command - wrong permissions, non-UTF-8
bytes, truncated JSON, the wrong shape - reached the operator as a Python
stack trace. `$AIRFLOW_HOME/production.json` is a generic enough name in a
shared directory that a foreign or half-written file is not far-fetched, and
none of those errors were something the CLI's own handler recognised.

The tolerance that lets `auth login` run without a credentials file now
covers an unusable one too, so the recovery the message recommends is
actually available.
@potiuk

potiuk commented Sep 25, 2026

Copy link
Copy Markdown
Member

Hello @Eason09053360 - thank you for your contributions to Apache Airflow!

The Airflow community has introduced a limit of 5 open pull requests at a time for contributors without write access to the repository. You currently have 33 open pull requests, so - as a one-time step of introducing the limit - we closed the ones where maintainers have not engaged yet:

These pull requests stay open because maintainers are already engaged in them - they count towards your limit:

This is not a judgement of you or of your changes. We never told contributors before that opening many pull requests at once was a problem, so there is nothing to feel bad about - and nothing is lost: your branches, commits and the review history stay where they are.

What we ask you to do is to make your first prioritization decision: choose which of the pull requests above matter most to you, and reopen them (up to 5 open at a time, including the ones still open) with the "Reopen pull request" button or gh pr reopen <PR_NUMBER> --repo apache/airflow. Reopen the ones you are ready to follow through - keep them rebased, respond to review comments and fix failing checks.

While your pull requests are waiting for review, the most valuable thing you can do is help in other ways - reviewing other contributors' pull requests, helping with issues, and taking part in the discussions on the devlist and Slack.

Why we introduced the limit, what it means for you and how to reopen or restore a pull request is explained in https://github.com/apache/airflow/blob/main/contributing-docs/32_open_pull_request_limit.rst.


Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting

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

Labels

area:airflow-ctl backport-to-airflow-ctl/v0-1-test closed because of open PR limit Closed as a one-time step of introducing the open pull request limit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants