JuliusBrussee/caveman · error

azure bearer credentials are unsupported; use an API key

Error message

azure bearer credentials are unsupported; use an API key

What it means

Azure OpenAI auth in this adapter supports only API keys (mapped to the api-key header). A credential whose scheme is bearer, whose value starts with 'Bearer ' or looks like a JWT ('eyJ...') is rejected outright: relabeling an Entra token as an api-key would silently fail auth upstream, so the adapter fails closed until bearer auth is properly supported. The synthetic 'no-key-required' marker is explicitly exempted.

Source

Thrown at proxy/providers/adapter.go:504

			out.Set("authorization", "Bearer "+credential.Key)
		}
		copyIfPresent(out, req.Header, "x-vertex-ai-llm-request-type")
		copyIfPresent(out, req.Header, "x-goog-user-project")
	case "azure_openai":
		if credential.Key != "" {
			// The standalone placeholder is a synthetic no-key marker used by
			// callers that explicitly allow env-backed API-key fallback. It is
			// not an Entra token and must not be forwarded as Authorization;
			// gateway fallback may replace it with the Azure api-key header.
			if credential.Scheme == "bearer" && credential.Key == "no-key-required" {
				break
			}
			// Real bearer/JWT credentials remain fail-closed until an auth-kind is
			// persisted and mapped end-to-end. They must never be silently
			// relabeled as an api-key.
			fields := strings.Fields(strings.TrimSpace(credential.Key))
			if credential.Scheme == "bearer" || len(fields) > 1 && strings.EqualFold(fields[0], "bearer") || strings.HasPrefix(strings.TrimSpace(credential.Key), "eyJ") {
				return nil, fmt.Errorf("azure bearer credentials are unsupported; use an API key")
			}
			out.Set("api-key", credential.Key)
		}
	default:
		if credential.Key != "" {
			out.Set("authorization", "Bearer "+credential.Key)
		}
	}
	return out, nil
}

func validGoogleQuotaProject(value string) bool {
	if value == "" || len(value) > 128 {
		return false
	}
	for _, r := range value {
		if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '.' || r == '_' || r == ':' {
			continue

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Use an Azure OpenAI API key for this provider: generate one in the Azure OpenAI resource (Keys and Endpoint) and configure it as the api-key credential.
  2. Strip any 'Bearer ' prefix or scheme metadata so the stored secret is the bare key value.
  3. Do not feed az account get-access-token output here — Entra bearer auth is deliberately unsupported.

Example fix

# before
providers:
  azure:
    credential_scheme: bearer
    key: eyJhbGciOiJSUzI1NiIs...   # Entra JWT -> error

# after
providers:
  azure:
    key: 32charAzureOpenAIResourceKey  # from Keys and Endpoint blade
Defensive patterns

Strategy: validation

Validate before calling

func isAzureAPIKeyCredential(scheme, key string) bool {
    if scheme == "bearer" {
        return false
    }
    fields := strings.Fields(strings.TrimSpace(key))
    if len(fields) > 1 && strings.EqualFold(fields[0], "bearer") {
        return false
    }
    return !strings.HasPrefix(strings.TrimSpace(key), "eyJ")
}

if !isAzureAPIKeyCredential(cred.Scheme, cred.Key) {
    return errors.New("Azure provider requires an API key, not an Entra/JWT token")
}

Type guard

func isAzureAPIKeyCredential(scheme, key string) bool {
    if scheme == "bearer" {
        return false
    }
    fields := strings.Fields(strings.TrimSpace(key))
    if len(fields) > 1 && strings.EqualFold(fields[0], "bearer") {
        return false
    }
    return !strings.HasPrefix(strings.TrimSpace(key), "eyJ")
}

Try / catch

if _, err := adapter.BuildUpstreamHeaders(credential, req); err != nil {
    if strings.Contains(err.Error(), "azure bearer credentials are unsupported") {
        http.Error(w, "configure an Azure OpenAI API key for this provider", http.StatusBadRequest)
        return
    }
    http.Error(w, err.Error(), http.StatusBadRequest)
}

Prevention

When it happens

Trigger: Configuring the Azure provider with credential_scheme: bearer; pasting an Entra ID / Azure AD token (they start with eyJ) into the api-key field; injecting OAuth credentials from a vault that prefixes values with 'Bearer '.

Common situations: Porting config from a tool that uses Azure AD auth (azure-cli login, managed identity) and assuming the same token works here; secrets managers that store tokens with their scheme prefix; copy-pasting a JWT from an Entra token dump.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/e7d6148918d8372a. Report an issue: GitHub.