Skip to content

apiProxy.defaultAiCreditsPricing is dropped during AWF config resolution, making the documented fallback inert #50017

Description

@lecoursen

Note

This issue was opened by GitHub Copilot CLI, acting on behalf of @lecoursen. The investigation and artifact analysis below were done by the agent.

Summary

models.default-ai-credits-pricing is documented as the escape hatch for models that are missing from AWF's built-in pricing table. It does not work. gh-aw compiles it into awf-config.json correctly, but AWF drops it during config resolution and never sets AWF_DEFAULT_AI_CREDITS_PRICING on the API proxy container, so the guard never sees a fallback and rejects every request with HTTP 400.

This makes the documented remedy for unknown_model_ai_credits a no-op.

Versions

  • gh-aw v0.83.4
  • AWF v0.27.42 (both the installed binary and the pinned container images)
  • Engine: copilot, model resolved to claude-opus-5

Symptom

400 Model "claude-opus-5" has no AI credits pricing and no default pricing is configured.
Set apiProxy.defaultAiCreditsPricing in the AWF config (e.g. {"input": 3.0, "output": 15.0})
to provide a fallback rate, or add the model to ...

All four copilot-harness attempts fail identically, so it is deterministic rather than transient.

Reproduction

Workflow frontmatter:

model: opus

models:
  default-ai-credits-pricing:
    input: 5.0
    output: 25.0

engine:
  id: copilot

Compile with gh-aw v0.83.4 and run. The opus alias resolves to claude-opus-5, which has no catalog pricing, and the run fails despite the fallback being configured.

Evidence from the run artifacts

1. gh-aw compiles the field correctly. agent/awf-config.json:

"apiProxy": {
  "enabled": true,
  "maxAiCredits": 1000,
  "maxCacheMisses": 5,
  "maxRuns": 500,
  "defaultAiCreditsPricing": { "input": 5, "output": 25 }
}

2. AWF does not propagate it to the container. In sandbox/firewall/audit/docker-compose.redacted.yml, every sibling apiProxy.* field arrives as an env var on the api-proxy service:

AWF_MAX_AI_CREDITS: 1000
AWF_MAX_RUNS: 500
AWF_MAX_CACHE_MISSES: 5
AWF_MODEL_ALIASES: {models:{...}}
AWF_RATE_LIMIT_ENABLED: 'false'

AWF_DEFAULT_AI_CREDITS_PRICING is absent.

3. The resolved config confirms the drop. sandbox/firewall/audit/awf-resolved-config.json has top-level keys including maxAiCredits, maxCacheMisses, maxRuns, modelAliases, and rateLimitConfig, but no defaultAiCreditsPricing key at all. So the loss happens during config resolution, before the env-mapping step.

4. The container-side code is fine. At tag v0.27.42, containers/api-proxy/guards/ai-credits-guard.js:

function checkUnknownModelRejection(model, provider = undefined) {
  const config = getAiCreditsConfig();
  if (!config.max) return null;
  if (!model) return null;
  if (config.defaultPricing) return null; // has fallback, don't reject
  ...
}

and getAiCreditsConfig() reads process.env.AWF_DEFAULT_AI_CREDITS_PRICING. Since that env var is never set, config.defaultPricing is null and the short-circuit never fires.

Why this looks like a resolution-layer bug

At v0.27.42 all the supporting pieces are present:

  • src/awf-config-schema.json defines defaultAiCreditsPricing
  • src/config-mapper.ts has defaultAiCreditsPricing: config.apiProxy?.defaultAiCreditsPricing
  • src/services/api-proxy-env-config.ts has ...(config.defaultAiCreditsPricing && { AWF_DEFAULT_AI_CREDITS_PRICING: JSON.stringify(config.defaultAiCreditsPricing) })

So the plumbing exists end to end, yet the value does not survive into the resolved config. Note that maxAiCredits reaches the container even though it is also set via CLI flag, which suggests the working fields may be arriving through the flag path rather than the config-file path, and that the config-file path is what is broken.

Impact

Any workflow pinned to, or resolving via alias to, a model newer than AWF's pricing catalog will fail with no working remedy. This is especially likely for alias users, because aliases like opus and sonnet deliberately resolve to the newest matching model, which is precisely the model most likely to be missing pricing. We hit it on a scheduled monthly workflow in github/docs-content and had to abandon the alias and pin an older model.

Suggested fixes

  1. Ensure apiProxy.defaultAiCreditsPricing survives config resolution and reaches AWF_DEFAULT_AI_CREDITS_PRICING, matching the documented behavior.
  2. Consider a CLI flag equivalent, mirroring how maxAiCredits is passed, so gh-aw has a path that is known to work.
  3. Separately, it would help a lot if new models inherited a sane default price rather than hard-failing, so that a newly released model does not break scheduled workflows on the day it ships.

Related

  • Downstream issue: https://github.com/github/docs-content/issues/23687
  • ADR that introduced the field: docs/adr/47687-models-default-ai-credits-pricing-for-byok-models.md
  • ADR on propagating it through imports: docs/adr/48746-propagate-imported-default-ai-credits-pricing.md

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions