Skip to content

ErrorEvent hides the nested error.name that arrives on the wire #5

Description

@mrwogu

Summary

ErrorEvent.error_type reports "Error" for every failure I have seen, which
identifies nothing. The notification behind the event carries a nested error
object whose name is a real discriminator, for example
LLMInvalidRequestError, and the SDK does not expose it anywhere.

That leaves free-text English as the only way to classify a failure, and the
wording is not stable.

Tested on 2026-08-02 with:

  • Droid CLI 0.186.0
  • droid-sdk-python==0.1.2
  • Factory protocol 1.147.0

What the wire carries

Frame captured by teeing ProcessTransport.read_messages, from a request for a
model the provider does not serve:

{"type": "error",
 "message": "Requested model was not found on the API provider",
 "errorType": "Error",
 "timestamp": "2026-08-02T11:00:13.647Z",
 "error": {"name": "LLMInvalidRequestError",
           "message": "Requested model was not found on the API provider"}}

What the SDK exposes

event.error_type  # 'Error'
event.message     # 'Requested model was not found on the API provider'

error.name is dropped. errorType is "Error" on the wire too, so that part
is a CLI concern, not an SDK mapping bug. The SDK-side ask is only to stop
discarding the nested object, which is strictly more informative than the field
currently surfaced.

Reproduction

async with client:
    await client.initialize_session(
        machine_id="probe",
        cwd="/tmp",
        mcp_servers=[],
        model_id="minimax-m2.5",   # any model the account cannot use
        interaction_mode=DroidInteractionMode.Auto,
        autonomy_level=AutonomyLevel.Off,
        skip_permissions_unsafe=False,
        enabled_tool_ids=[],
    )
    await client.add_user_message(text="Reply with exactly: OK")
    async for event in client.receive_response():
        if isinstance(event, ErrorEvent):
            print(event.error_type, "|", event.message)

Impact

Without a discriminator, a client has to regex-match English prose to decide
whether a failure is the caller's fault or the platform's. In my bridge that
decision is the difference between HTTP 404 and HTTP 502.

The same condition, the same model, produced three different wordings within a
day:

Model not allowed by organization policy
404 {"error":{"message":"Model not found, inaccessible, and/or not deployed","param":"model","code":"NOT_FOUND","type":"error"}}
Requested model was not found on the API provider

The third one broke my matcher and turned 7 requests in one run into reported
bridge failures, when the real cause was a model the provider does not serve.
Had error.name been available, none of that matching would have been needed.

Forwarding error_type to end users is not an option either: an
OpenAI-compatible surface would have to report "type": "Error", which means
nothing to a client, so I now discard it and substitute my own value.

Suggested fix

Expose the nested error on ErrorEvent, for example as error_name plus the
raw nested payload, so clients can branch on LLMInvalidRequestError instead of
parsing prose.

A stable error code on the protocol side would be the real fix, and I am raising
that with the CLI separately, but exposing what already arrives would help
today.

Metadata

Metadata

Assignees

No one assigned

    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