github/spec-kit · error · ValueError
providers[{i}]: auth='azure-ad' requires 'tenant_id', 'clien
Error message
providers[{i}]: auth='azure-ad' requires 'tenant_id', 'client_id', and 'client_secret_env' What it means
Raised for `auth: azure-ad` when any of `tenant_id`, `client_id`, or `client_secret_env` is missing/falsy in the provider entry. The Azure AD client-credentials flow needs all three, where the client secret is referenced indirectly through an environment variable name.
Source
Thrown at src/specify_cli/authentication/config.py:188
)
if auth not in _prov.supported_auth_schemes:
raise ValueError(
f"providers[{i}]: provider {provider!r} does not support "
f"auth scheme {auth!r}; supported: {list(_prov.supported_auth_schemes)}"
)
# Validate token source based on auth scheme
if auth in ("bearer", "basic-pat"):
if not token and not token_env:
raise ValueError(
f"providers[{i}]: auth={auth!r} requires 'token' or 'token_env'"
)
elif auth == "azure-ad":
tenant_id = entry_raw.get("tenant_id")
client_id = entry_raw.get("client_id")
client_secret_env = entry_raw.get("client_secret_env")
if not all([tenant_id, client_id, client_secret_env]):
raise ValueError(
f"providers[{i}]: auth='azure-ad' requires "
"'tenant_id', 'client_id', and 'client_secret_env'"
)
for field_name, field_val in [
("tenant_id", tenant_id),
("client_id", client_id),
("client_secret_env", client_secret_env),
]:
if not isinstance(field_val, str) or not field_val.strip():
raise ValueError(
f"providers[{i}]: '{field_name}' must be a non-empty string"
)
# azure-cli needs no extra fields
entries.append(
AuthConfigEntry(
hosts=tuple(hosts),
provider=provider,View on GitHub (pinned to bf88c9f9a8)
Solutions
- Set all three keys: `tenant_id`, `client_id`, `client_secret_env` (name of the env var holding the secret)
- Export the secret in the environment under the name given to `client_secret_env`
- If you only have a PAT, use `auth: basic-pat` with `token_env` instead of azure-ad
Example fix
# before - hosts: ["*.visualstudio.com"] provider: azure-devops auth: azure-ad tenant_id: <tid> # after - hosts: ["*.visualstudio.com"] provider: azure-devops auth: azure-ad tenant_id: <tid> client_id: <cid> client_secret_env: AZURE_CLIENT_SECRET
Defensive patterns
Strategy: validation
Validate before calling
if entry.get("auth") == "azure-ad":
missing = [k for k in ("tenant_id", "client_id", "client_secret_env") if not entry.get(k)]
if missing:
raise SystemExit(f"azure-ad entry missing: {missing}") Type guard
def is_complete_azure_ad(entry: dict) -> bool:
if entry.get("auth") != "azure-ad":
return True
return all(isinstance(entry.get(k), str) and entry[k].strip()
for k in ("tenant_id", "client_id", "client_secret_env")) Try / catch
try:
load_auth_config(raw)
except ValueError as exc:
if "auth='azure-ad' requires" in str(exc):
# collect the three fields (e.g. via az ad sp create-for-rbac) and retry
raise
raise Prevention
- azure-ad needs exactly tenant_id + client_id + client_secret_env; the raw secret never goes in the file, only its env var name
- Generate service-principal credentials with `az ad sp create-for-rbac` and export the secret env var first
When it happens
Trigger: An `auth: azure-ad` entry where one or more of the keys `tenant_id`, `client_id`, `client_secret_env` are absent, `null`, or empty strings — the `all([...])` check treats falsy values as missing.
Common situations: Migrating a bearer entry to azure-ad and only adding `tenant_id`; putting the secret value in `client_secret` instead of `client_secret_env` (the library deliberately never stores the raw secret); Azure DevOps PAT setups that actually want `basic-pat`.
Related errors
- providers[{i}]: '{field_name}' must be a non-empty string
- providers[{i}]: invalid host pattern {h!r}. Only exact hostn
- providers[{i}]: 'provider' must be a non-empty string
- providers[{i}]: 'auth' must be a non-empty string
- providers[{i}]: 'token' must be a non-empty string
AI-assisted analysis of github/spec-kit@bf88c9f9a8 (2026-08-14).
Data as JSON: /api/errors/3351fe275cc5fed5.
Report an issue: GitHub.