langchain-ai/deepagents · error · RuntimeError

Device code response from

Error message

Device code response from {device_code_url} is missing required fields: {exc}

What it means

After a successful device-code HTTP request, the JSON body is validated against the _DeviceCodeResponse schema (verification_uri, user_code, expires_in, etc.); missing/invalid fields raise this RuntimeError with the pydantic ValidationError details. The provider responded 200 but with a body that does not match the RFC 8628 device authorization response.

Solutions

  1. Inspect the ValidationError details in the message to see which required fields are missing and compare against the provider's device-flow docs.
  2. Verify you are hitting the real provider endpoint (not a proxy/captive-portal page) - curl the URL and check the raw JSON.
  3. If the provider is genuinely non-standard, use its authorization_code flow instead of the device flow, or update the server's OAuth metadata.

Example fix

// before (provider response)
{"device_code": "..."}  # missing user_code/verification_uri
// after
check endpoint: curl -s https://github.com/login/device/code -d client_id=...  # expect full RFC 8628 payload incl. user_code, verification_uri, expires_in
Defensive patterns

Strategy: validation

Validate before calling

import httpx, json
resp = httpx.post(device_code_url, data={"client_id": client_id})
body = resp.json()  # raises if HTML/captive-portal body
required = {"device_code", "user_code", "verification_uri", "expires_in"}
missing = required - body.keys()
assert not missing, f"provider response missing fields: {missing}"

Type guard

def is_valid_device_response(body: object) -> TypeGuard[dict]:
    required = {"device_code", "user_code", "verification_uri", "expires_in"}
    return isinstance(body, dict) and required.issubset(body.keys())

Try / catch

try:
    token = await _run_device_flow(...)
except RuntimeError as e:
    if str(e).startswith("Device code response"):
        print(f"Provider returned a non-standard device response: {e}. Check proxy/portal and provider docs.")
    else:
        raise

Prevention

When it happens

Trigger: _run_device_flow calls _DeviceCodeResponse.model_validate(response.json()) and validation fails - missing required fields, wrong types, or non-JSON body from the device endpoint.

Common situations: Provider returning an HTML error page with 200 behind a captive portal/proxy, a non-standard provider omitting fields like verification_uri_complete, an API version change, or an intercepted response from a corporate proxy.

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


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/555c79d5a0261bd9. Report an issue: GitHub.

Appendix: source

Thrown at libs/code/deepagents_code/mcp_auth.py:1918

            data=init_data,
            headers={"Accept": "application/json"},
        )
        try:
            response.raise_for_status()
        except httpx.HTTPStatusError as exc:
            msg = (
                f"Device code request failed: HTTP {response.status_code} "
                f"from {device_code_url}."
            )
            raise RuntimeError(msg) from exc
        try:
            device = _DeviceCodeResponse.model_validate(response.json())
        except (ValueError, ValidationError) as exc:
            msg = (
                f"Device code response from {device_code_url} is missing "
                f"required fields: {exc}"
            )
            raise RuntimeError(msg) from exc

        await interaction.show_device_code(
            verification_uri=device.verification_uri,
            user_code=device.user_code,
            expires_in=device.expires_in,
        )

        interval = max(device.interval, 1)
        loop = asyncio.get_running_loop()
        deadline = loop.time() + device.expires_in
        while loop.time() < deadline:
            await asyncio.sleep(interval)
            token_response = await client.post(
                token_url,
                data={
                    "client_id": client_id,
                    "device_code": device.device_code,
                    "grant_type": "urn:ietf:params:oauth:grant-type:device_code",

View on GitHub (pinned to a1af029e6e)