Skip to content

[Bug]: GitHub Copilot provider: all models report text-only input — image attachments blocked #2941

Closed

Description

@DamnUi

Client or integration

Codex App

Area

Catalog / models

Summary

When using the github-copilot provider, every modeli s cataloged with inputModalities: ["text"], which makes Codex block image attachments with "This model does not support image inputs." This affects all 32+ vision-capable models (Claude Opus/Sonnet, GPT-4o/4.1/5.x, Gemini, Grok, etc.).

Expected: models that support vision should be cataloged with ["text", "image"] so users can attach images.

Root cause

Three compounding gaps in the github-copilot provider:

1. Copilot API uses a non-standard capabilities structure

GitHub Copilot's /models endpoint reports vision support in a nested format that OpenCodex does not parse:

{
  "id": "claude-opus-4.6",
  "capabilities": {
    "supports": { "vision": true },
    "limits": { "vision": { "max_prompt_images": 20 } }
  }
}

OpenCodex's modelInputModalities() function (in provider-fetch.ts) checks:

  • item.input_modalities / item.modalities — Copilot does not have these fields
  • capabilityRecord?.vision — but capabilityRecord is plainRecord(item.capabilities) which yields { supports: {...}, limits: {...} }, so .vision is undefined (it is nested under .supports)
  • capabilities array from modelCapabilities() — iterates capabilityFields looking for value === true, but supports is an object not true

Result: every path returns undefined, the fallback chain lands on ["text"], and Codex blocks images.

2. No modelInputModalities in the registry entry

The github-copilot entry in src/providers/registry.ts (line 2694) has no modelInputModalities field. Other major providers include one as a cold-start seed.

3. Not in PROVIDER_ALIASES

github-copilot is absent from PROVIDER_ALIASES in src/generated/model-metadata.ts. Providers like openrouter are aliased, so their models can fall back to the metadata database. Without an alias, this fallback is also dead.

Reproduction

  1. Install OpenCodex 2.33.0 and log in via the github-copilot provider
  2. Select any vision-capable model (e.g. claude-opus-4.6, gpt-4o, gpt-5.4)
  3. Try to attach/paste an image in the Codex App
  4. Codex blocks it with: "This model does not support image inputs"
  5. Compare: switch to the same model via openrouter provider — images work fine

Proposed fix

parse the nested Copilot structure

In modelInputModalities() in provider-fetch.ts, add a check for the nested capabilities.supports.vision structure:

const supports = plainRecord(capabilityRecord?.supports);
if (supports?.vision === true) return ["text", "image"];

This would automatically handle all current and future Copilot models.

or the eaiser way is to just

Add modelInputModalities to the github-copilot registry entry.

Version

2.33.0

OS

macOS 26.2 (arm64)

Provider and model

github-copilot / claude-opus-4.6 (affects all 32 vision-capable models on the Copilot API)

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    catalogModel catalog, slugs, visibility, routed entries
    on Aug 29, 2026
  2. Ingwannu commented on Aug 29, 2026

    @Ingwannu
    Owner

    Confirmed on current dev at 3a9835ca91d34affe97a4028b616d33e86d09d5b.

    The report is valid: catalogHintsFromModelsApiItem() reads direct capabilities.vision, but Copilot publishes the signal as capabilities.supports.vision. The nested object is not converted into the flat capability list, so discovery contributes no image modality and the routed catalog falls back to text-only.

    I am preparing the narrow fix in the shared live-discovery parser:

    • honor an explicit nested supports.vision: true as ["text", "image"];
    • honor supports.vision: false as ["text"];
    • ignore malformed/unknown nested values so they cannot over-advertise image support;
    • add focused regression coverage for all three cases.

    I will not add a blanket Copilot registry seed or provider alias: the live per-model capability is the more precise source and avoids incorrectly marking text-only Copilot models as vision-capable. I will link the implementation PR here after focused validation.

  3. DamnUi commented on Aug 29, 2026

    @DamnUi
    ContributorAuthor

    Ignore the pr pelase

  4. lidge-jun commented on Aug 29, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 73 / 80

    이 이슈는 지금 dev HEAD 3a9835ca9 (#2933) 에서도 그대로인 GitHub Copilot 카탈로그 구멍이다. Codex 앱에서 github-copilot 모델을 고르면, 그림이 되는 모델까지 전부 inputModalities: ["text"] 로 적히고, 이미지를 붙이면 "This model does not support image inputs" 로 막힌다. 작성자는 Claude Opus/Sonnet, GPT-4o/4.1/5.x, Gemini, Grok 같은 32개 이상 그림 모델이 같이 죽는다고 했다. 같은 모델을 openrouter 로 고르면 그림이 된다. 미리보기 배포는 계획에 없고, types.ts/config.ts 분할과도 안 겹친다. 독립 버그픽스다.

    지금 HEAD 가 그림을 못 읽는 이유는 세 겹이다. 첫째, Copilot /models 는 capabilities.supports.vision 을 쓴다. src/codex/catalog/provider-fetch.ts 의 modelInputModalities() (1063-1108행) 는 plainRecord(item.capabilities)?.vision 만 본다. 객체는 { supports, limits } 이라 .vision 은 없다. modelCapabilities() 도 값이 true 인 칸만 이름 목록에 넣어서, supports 객체는 목록에 vision 을 안 남긴다. 힌트가 비면 src/codex/catalog/parsing.ts 457-468행 ensureStrictCatalogFields 가 ["text"] 를 채운다. 둘째, 레지스트리 github-copilot 항목(src/providers/registry.ts 2790행)에 modelInputModalities 씨앗이 없다. 산 신호가 죽으면 차가운 시작도 글자만이다. 셋째, github-copilot 은 PROVIDER_ALIASES 에도 없어서 메타데이터 데이터베이스 폴백도 안 탄다. 메인테이너가 같은 SHA 에서 이 보고가 맞다고 확인했다.

    고치는 바른 길은 산 발견 파서에 겹친 신호를 넣는 것이다. supports.vision === true 면 ["text", "image"] 이고, === false 면 ["text"] 이고, 이상한 값은 그림을 켜면 안 된다. 레지스트리에 그림 씨앗을 통째로 심거나 별칭을 넣으면, Copilot 이 글자만이라고 한 15개 모델까지 그림으로 보일 수 있다. 메인테이너가 그 두 갈래를 안 하겠다고 한 이유가 그것이다. 작성자가 연 #2942 는 참 갈래만 넣었고, 거짓 갈래와 테스트가 없으며, 갈래가 main 이라 이미 닫혔다. 그 PR로 이 이슈를 닫으면 안 된다.

    테스트 자리는 이미 있다. tests/catalog-input-modality-enum.test.ts 가 catalogHintsFromModelsApiItem 을 부르고, tests/catalog-llamacpp-capabilities.test.ts 가 같은 함수로 llama.cpp multimodal (#1797) 을 잠근다. Copilot 모양 세 줄(true / false / 이상한 값)을 거기에 넣으면 hygiene 도 통과한다. limits.vision.max_prompt_images 는 이번 첨부를 켜는 신호와 다른 칸이다. 이번 이슈는 모달리티 오탐이다.

    경로 src/codex/catalog/provider-fetch.ts modelInputModalities - 겹친 supports.vision 을 안 본다. 산 발견이 그림을 못 남기는 바로 그 줄이다.
    경로 src/codex/catalog/provider-fetch.ts modelCapabilities - 겹친 객체를 평평한 이름 목록으로 안 펼친다. 첨부는 모달리티가 막으므로 1차 고침은 모달리티만으로도 된다. 능력 목록은 후속이다.
    경로 src/providers/registry.ts github-copilot - modelInputModalities 씨앗이 없다. 산 신호가 죽은 지금, 차가운 시작도 글자만이다. 씨앗을 통째로 심으면 글자만 모델이 그림으로 보인다. 넣지 않는 편이 맞다.
    경로 src/codex/catalog/parsing.ts 457-468 - 힌트가 비면 ["text"] 로 채운다. 빈 목록보다 낫다. 이 기본값을 바꾸면 다른 제공자까지 흔들린다. 고칠 곳은 여기가 아니다.
    경로/심볼 - #2942 - 참 갈래만 있는 닫힌 초안이다. 이 이슈를 그 PR로 닫지 마세요.

    메인테이너의 판단이 필요한 지점

    • 이미 말한 대로 산 파서만 고칠지, 레지스트리 씨앗/별칭도 넣을지. 산 파서만 권장
    • modelCapabilities() 펼치기를 같은 PR에 넣을지, 모달리티 다음에 둘지
    • 구현 PR이 초록이 되면 이 이슈를 그 PR로 닫을지

    너의 추천
    열린 채로 두고, 메인테이너가 준비 중인 dev 위 구현을 받으세요. 참/거짓/이상한 값 세 갈래와 catalogHintsFromModelsApiItem 회귀를 같이 넣어야 한다. #2942 는 다시 열지 마세요. 레지스트리 씨앗과 별칭은 넣지 마세요. types/config 분할과 무관하니 close-don't-rebase 대상이 아니다. 구현이 dev 에 들어가면 이 이슈를 그 PR로 닫으면 된다.

    이 댓글은 grok-bot이 작성했습니다

  5. Ingwannu commented on Aug 29, 2026

    @Ingwannu
    Owner

    The fix is now available in #2943 at exact head 2fe8c89dd41d83dc34edbd90b06ddff83483ff1b.

    Focused discovery coverage passes: 31 tests / 180 expectations. The PR is intentionally not merged yet: cross-platform CI is running and, because I authored it, project policy requires approval from another maintainer. @lidge-jun has been requested for review.

  6. Ingwannu commented on Aug 29, 2026

    @Ingwannu
    Owner

    Update: #2943 was rebased after #2863 advanced dev and touched the shared catalog file. The current exact head is b2cb71b992d893fe1a955d3096a03c06ce4e038a; the focused discovery suite passes again at 31 tests / 182 expectations. The force-push was limited to the PR branch, so review and CI correctly restarted for the new head.

  7. lidge-jun commented on Aug 29, 2026

    @lidge-jun
    Owner

    Fixed on dev as 3700526489645f65f7bf162fd4ffa81e610fa3f1 (#2943). Vision-capable github-copilot models will be cataloged with ["text", "image"] and accept image attachments after the next catalog sync.

    Your diagnosis was right on all three points, and the first one is what got fixed. modelInputModalities() now reads the nested capabilities.supports.vision boolean, so every current and future Copilot model that reports vision gets image input without anyone maintaining a list. An explicit nested false is honoured as a real text-only denial rather than falling through.

    Two notes on the parts of your proposal we did not take, since you clearly thought about them.

    The registry seed — your "easier way" — we skipped. modelInputModalities is a per-model map, so a seed means asserting a model-by-model list. Nobody here has an audited one, and writing it from the 32 names in your report would duplicate a remote catalog that changes without us and go stale silently. Parsing what Copilot reports is authoritative per model. A selective seed for verified ids is still legitimate as cold-start protection if someone can audit the list — that stays open.

    PROVIDER_ALIASES we did not touch, because it turned out not to be needed. That path exists to fall back to the bundled metadata database when a provider reports nothing useful; here Copilot does report the capability, we just were not reading it. Fixing the parse makes the fallback unnecessary rather than dead.

    One detail from your payload that shaped the implementation: capabilities carries a second vision key under limits holding max_prompt_images. Any fix that searched the capabilities object loosely for a vision-ish key would have found that record — truthy, and meaningless as a capability signal. Including it in your report is what made the exact-path read obviously correct, so thank you for pasting the whole shape rather than a summary.

    On #2942: it was closed only because it targeted main instead of dev — the branch gate is automatic and not a judgement on the change. The parse that shipped is essentially yours. If you open another PR, target dev and it will go through normal review.

    A follow-up in #2944 adds regression coverage for the precedence rule, the limits.vision sibling, and malformed capability shapes.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcatalogModel catalog, slugs, visibility, routed entries

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions