Skip to content

api-proxy: middle_power modelFallback rewrites Azure OpenAI deployment names to base-catalog names, causing HTTP 404 DeploymentNotFound on BYOK targets #3975

Description

@zarenner

Summary

The api-proxy sidecar's apiProxy.modelFallback config defaults to {enabled: true, strategy: middle_power}. When Copilot CLI sends a model that is not present in the cached provider catalog, the proxy rewrites the request body's model field to a family-matched median-power catalog model. For BYOK Azure OpenAI deployments — where COPILOT_MODEL is the user-defined deployment name rather than the upstream catalog name — this rewrite causes Azure to reject the request with HTTP 404 DeploymentNotFound. This is the 404 symptom reported in gh-aw #35483.

Root cause

  • containers/api-proxy/model-resolver.js runs tryMiddlePowerFallback for any model not found in the cached catalog.
  • Default config (from src/awf-config-schema.json ~L163–182, applied by buildApiProxyService() in src/services/api-proxy-service.ts ~L86–170):
    { "apiProxy": { "modelFallback": { "enabled": true, "strategy": "middle_power" } } }
  • For non-githubcopilot BYOK targets (Azure OpenAI, custom OpenAI-compatible), the deployment name is opaque to the proxy — no remote catalog exists to reconcile against. The proxy still applies middle_power and replaces the user-specified deployment name with a base-catalog name (e.g. gpt-5-mini-zarenner-testgpt-5-mini-2025-08-07).

Reproduction

  1. Configure a Copilot BYOK workflow against Azure OpenAI with a deployment name that differs from the catalog name, e.g. gpt-5-mini-zarenner-test (Azure deployment of base gpt-5-mini-2025-08-07):
    engine:
      id: copilot
      env:
        COPILOT_PROVIDER_BASE_URL: "https://<resource>.openai.azure.com/openai/v1"
        COPILOT_MODEL: gpt-5-mini-zarenner-test
        COPILOT_PROVIDER_API_KEY: ${{ secrets.AZURE_OPENAI_API_KEY }}
  2. With companion gh-aw auth workarounds applied (see gh-aw #35483 and sibling gh-aw issues), so the request actually reaches Azure, observe HTTP 404:
    {"error":{"code":"DeploymentNotFound","message":"The API deployment for this resource does not exist..."}}
  3. Inspect awf-reflect.json from the audit logs: model_fallback.enabled: true.
  4. Direct curl to Azure with the deployment name returns HTTP 200 — proving it is the proxy rewrite, not Azure or the request shape, that produces the 404.

Workaround

Set apiProxy.modelFallback.enabled: false in awf-config.json. This isn't currently exposed by gh-aw's compiler (filed separately on the gh-aw side), so a post-compile sed against the lock file is required today:

sed -i.bak \
  's|"apiProxy":{"enabled":true,|"apiProxy":{"enabled":true,"modelFallback":{"enabled":false,"strategy":"middle_power"},|' \
  .github/workflows/<name>.lock.yml

Verified by re-running and confirming awf-reflect.json now shows model_fallback.enabled: false and api-proxy otel logs show status 200. The agent successfully completed an Azure OpenAI BYOK round-trip with this patch.

Proposed fixes (options)

  1. Default enabled: false for non-githubcopilot BYOK targets. When the resolved upstream hostname is not the official Copilot API, middle_power rewriting is almost always wrong because no shared catalog exists. (Minimal-impact option.)
  2. Default enabled: false globally and require explicit opt-in. The feature is useful for the official Copilot catalog but a silent-failure footgun on any custom provider.
  3. Strict pass-through heuristic: when model is missing from the cached catalog and the target is a custom provider, pass the model name through unchanged rather than rewriting.

Context

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions