Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.2
What happened and how to reproduce it?
Issue Description
With multi_team = True, connections that have team_name set (e.g. Portal) cannot be tested from the UI.
Airflow 3.1.7+ includes a fix for masked passwords (***) in /connections/test (#59643): the API tries to load the existing connection via Connection.get_connection_from_secrets(conn_id) and then merges the form body with update_orm_from_pydantic, restoring the real password when the UI sends ***.
That lookup does not pass team_name. For team-scoped connections, get_connection_from_secrets raises AirflowNotFoundException even though the connection exists in the metastore. The API then falls back to constructing a connection from the request body only, which still contains password="***", so the test authenticates with the literal masked placeholder and fails.
This happens for both admin and team users. The stored credentials are correct: the same connection works with PostgresHook / DAG runtime when the team context is available.
Steps to reproduce:
- Enable multi-team mode (
[core] multi_team = True) with at least one team (e.g. Portal) and a DAG bundle with team_name="Portal".
- Create a Postgres connection in the UI:
conn_id: recruitment_crm_db (any id)
- valid host/login/password
- assign
team_name = Portal
- Confirm the row exists in the metadata DB with
team_name='Portal'.
- Open the connection in the UI (as admin or as a Portal user) and click Test without re-entering the password (password field shows masked
***).
- Observe authentication failure (e.g.
password authentication failed).
Repro evidence (same deployment):
from airflow.models.connection import Connection
from airflow.secrets.metastore import MetastoreBackend
from airflow.providers.postgres.hooks.postgres import PostgresHook
from airflow.settings import Session
c = Session().query(Connection).filter(Connection.conn_id == "recruitment_crm_db").one()
# team_name == "Portal", password decrypts correctly
PostgresHook(connection=c).test_connection()
# -> (True, "Connection successfully tested")
MetastoreBackend().get_connection("recruitment_crm_db")
# -> None
MetastoreBackend().get_connection("recruitment_crm_db", team_name="Portal")
# -> Connection(...)
Connection.get_connection_from_secrets("recruitment_crm_db")
# -> AirflowNotFoundException: The conn_id `recruitment_crm_db` isn't defined
### What you think should happen instead?
---
### What you think should happen instead?
```markdown
`/connections/test` should successfully test team-scoped connections when the caller is allowed to access that team.
Expected behavior:
1. Resolve the existing connection using the correct team context (or otherwise include team-scoped metastore rows), so `update_orm_from_pydantic` can restore the real password when the UI sends the masked placeholder `***`.
2. Alternatively, detect `password == "***"` (masked value) and never treat it as a real credential.
3. UI Test should succeed for a valid team-owned Postgres connection without requiring the user to re-type the password.
Current behavior is erroneous because:
- the connection exists and credentials are valid;
- DAG/hook usage works;
- only UI Test fails due to secrets lookup without `team_name`, then auth with literal `***`.
### Operating System
Debian GNU/Linux 12 (bookworm) # inside official apache/airflow container
### Deployment
Docker-Compose
### Apache Airflow Provider(s)
_No response_
### Versions of Apache Airflow Providers
apache-airflow-providers-postgres==6.7.0
apache-airflow-providers-common-sql==2.0.0
apache-airflow-providers-common-compat==1.15.0
apache-airflow-providers-common-io==1.7.2
apache-airflow-providers-common-messaging==2.0.3
### Official Helm Chart version
Not Applicable
### Kubernetes Version
Not Applicable
### Helm Chart configuration
Not Applicable
### Docker Image customizations
Based on official image:
```dockerfile
FROM apache/airflow:3.2.2
USER root
COPY requirements.txt /requirements.txt
RUN chown -R airflow:root /requirements.txt
USER airflow
RUN pip install --no-cache-dir -r /requirements.txt
requirements.txt only adds DAG libs (jira, pymysql, python-gitlab) — not related to this bug. Relevant config:
[core]
multi_team = True
test_connection = Enabled
DAG bundle example:
[{"name":"company-dags","classpath":"airflow.dag_processing.bundles.local.LocalDagBundle","kwargs":{"path":"/opt/airflow/dags/company"},"team_name":"Company"}]
### Anything else?
### Anything else?
```markdown
Every time for any saved connection with non-null `team_name` when using UI Test without re-entering the password.
Workarounds:
1. Re-type the real password in the UI form before clicking Test.
2. Temporarily set `team_name = NULL` on the connection (loses team isolation).
3. Validate with PostgresHook / DAG instead of UI Test.
Related:
- #59298 / #59643 / #61670 — masked password fix (works for non-team connections)
- This report is specifically about the remaining gap when `multi_team=True` and connection.team_name is set.
### Are you willing to submit PR?
- [ ] 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)
Under which category would you file this issue?
Airflow Core
Apache Airflow version
3.2.2
What happened and how to reproduce it?
Issue Description
With
multi_team = True, connections that haveteam_nameset (e.g.Portal) cannot be tested from the UI.Airflow 3.1.7+ includes a fix for masked passwords (
***) in/connections/test(#59643): the API tries to load the existing connection viaConnection.get_connection_from_secrets(conn_id)and then merges the form body withupdate_orm_from_pydantic, restoring the real password when the UI sends***.That lookup does not pass
team_name. For team-scoped connections,get_connection_from_secretsraisesAirflowNotFoundExceptioneven though the connection exists in the metastore. The API then falls back to constructing a connection from the request body only, which still containspassword="***", so the test authenticates with the literal masked placeholder and fails.This happens for both admin and team users. The stored credentials are correct: the same connection works with
PostgresHook/ DAG runtime when the team context is available.Steps to reproduce:
[core] multi_team = True) with at least one team (e.g.Portal) and a DAG bundle withteam_name="Portal".conn_id:recruitment_crm_db(any id)team_name = Portalteam_name='Portal'.***).password authentication failed).Repro evidence (same deployment):