github/copilot-sdk · error · ValueError
Missing required fields in ModelInfo: id=
Error message
Missing required fields in ModelInfo: id={id}, name={name}, capabilities={capabilities_dict} What it means
ModelInfo.from_dict requires 'id', 'name', and 'capabilities' in each model entry of the model catalog and raises ValueError listing the missing values otherwise. Optional 'policy' is decoded only when present, but id/name/capabilities are mandatory to build a usable ModelInfo.
Solutions
- Upgrade/align the server so every model entry includes id, name, and capabilities
- Skip or repair incomplete entries before calling from_dict (validate keys first)
- Update mocks/fixtures to include a capabilities object compatible with ModelCapabilities.from_dict
Example fix
// before (fixture)
{"id": "gpt-4o", "name": "GPT-4o"}
// after
{"id": "gpt-4o", "name": "GPT-4o", "capabilities": {"supports": {}}}
# (capabilities object must satisfy ModelCapabilities.from_dict) Defensive patterns
Strategy: validation
Validate before calling
def model_entry_is_decodable(m: dict) -> bool:
return (
isinstance(m, dict)
and m.get("id") is not None
and m.get("name") is not None
and m.get("capabilities") is not None
)
models = [ModelInfo.from_dict(m) for m in raw if model_entry_is_decodable(m)] Type guard
def is_model_entry(obj: object) -> bool:
return isinstance(obj, dict) and {"id", "name", "capabilities"} <= obj.keys() Try / catch
try:
info = ModelInfo.from_dict(entry)
except ValueError as e:
if str(e).startswith("Missing required fields in ModelInfo"):
logger.warning("skipping malformed model entry: %s", entry)
info = None
else:
raise Prevention
- Validate each catalog entry's keys before decoding
- Keep client and server model-catalog schemas versioned together
- Fixtures must include a capabilities object matching ModelCapabilities.from_dict
When it happens
Trigger: The list-models response contains an entry missing 'id', 'name', or 'capabilities'; a minimal mock returns only {'id': ..., 'name': ...}; a server-side schema change renames or drops 'capabilities'.
Common situations: Version drift between client schema expectations and server model catalog; aggregating models from multiple backends where one omits capabilities; hand-written test fixtures.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Missing required fields in PingResponse: message=
- Missing required field 'message' in StopError
- Missing required fields in GetStatusResponse: version=
- Missing required field 'isAuthenticated' in…
- Missing required fields in ModelPolicy: state=
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/ac59922c434eb339.
Report an issue: GitHub.
Appendix: source
Thrown at python/copilot/client.py:1144
id: str # Model identifier (e.g., "claude-sonnet-4.5")
name: str # Display name
capabilities: ModelCapabilities # Model capabilities and limits
policy: ModelPolicy | None = None # Policy state
billing: ModelBilling | None = None # Billing information
# Supported reasoning effort levels (only present if model supports reasoning effort)
supported_reasoning_efforts: list[str] | None = None
# Default reasoning effort level (only present if model supports reasoning effort)
default_reasoning_effort: str | None = None
@staticmethod
def from_dict(obj: Any) -> ModelInfo:
assert isinstance(obj, dict)
id = obj.get("id")
name = obj.get("name")
capabilities_dict = obj.get("capabilities")
if id is None or name is None or capabilities_dict is None:
raise ValueError(
f"Missing required fields in ModelInfo: id={id}, name={name}, "
f"capabilities={capabilities_dict}"
)
capabilities = ModelCapabilities.from_dict(capabilities_dict)
policy_dict = obj.get("policy")
policy = ModelPolicy.from_dict(policy_dict) if policy_dict else None
billing_dict = obj.get("billing")
billing = ModelBilling.from_dict(billing_dict) if billing_dict else None
supported_reasoning_efforts = obj.get("supportedReasoningEfforts")
default_reasoning_effort = obj.get("defaultReasoningEffort")
return ModelInfo(
id=str(id),
name=str(name),
capabilities=capabilities,
policy=policy,
billing=billing,
supported_reasoning_efforts=supported_reasoning_efforts,
default_reasoning_effort=default_reasoning_effort,View on GitHub (pinned to cd8cf15dc3)