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

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions