You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SDK errors are ~11 disjoint Error subclasses (plus v0.3's separate A2AError) wired together by 6+ parallel error.name-keyed lookup tables and duplicated instanceof chains in each transport. Consequences:
No shared base — client and server rebuild the same mappings.
Adding an error class means touching 6+ tables and 3 mapping chains.
The two dimensions callers need to discriminate on — what went wrong (semantic) and which wire carried it (transport) — are orthogonal. JS single-inheritance means a class tree has to pick one dimension as parent. The proposal below picks semantic as the class axis and models transport as interfaces + user-defined type guards, so both dimensions remain reachable.
Proposal
Class axis is semantic. A2AError is the shared base; each spec-aligned error extends it; each transport publishes a concrete per-transport subclass of every semantic error. Transport-native context is expressed as an interface implemented by all subclasses of a given transport, and narrowed at catch time via user-defined type guards (isRestError, isGrpcError, isJsonRpcError).
Per-transport concrete subclasses (RestTaskNotFoundError, GrpcTaskNotFoundError, JsonRpcTaskNotFoundError, and analogues for every semantic error) — extend the corresponding semantic class and implement the transport interface.
Transport interfaces (RestA2AError, GrpcA2AError, JsonRpcA2AError) — declare transport-native context:
RestA2AError: statusCode, headers, cause
GrpcA2AError: status, statusDetailsBin (no cause on the wire)
JsonRpcA2AError: envelopeCode, data
Type guards (isRestError(e), isGrpcError(e), isJsonRpcError(e)) — user-defined type predicates that narrow an A2AError to the transport interface.
Catch-site ergonomics
try{awaitclient.getTask({ id });}catch(e){if(einstanceofTaskNotFoundError){// Transport-agnostic: e.taskId is available.if(isRestError(e)){// Narrowed: e.statusCode, e.headers, e.cause are typed.if(e.statusCode===429)backoff(e.headers['retry-after']);}}}
The TS compiler intersects the class type with the interface type inside the guard, so both semantic fields (taskId) and transport fields (statusCode) are simultaneously accessible.
Problem
SDK errors are ~11 disjoint
Errorsubclasses (plus v0.3's separateA2AError) wired together by 6+ parallelerror.name-keyed lookup tables and duplicatedinstanceofchains in each transport. Consequences:cause, and gRPCgrpc-status-details-binmetadata never reach the caller (see [Feat]: Passthrough http status code in error #317).The two dimensions callers need to discriminate on — what went wrong (semantic) and which wire carried it (transport) — are orthogonal. JS single-inheritance means a class tree has to pick one dimension as parent. The proposal below picks semantic as the class axis and models transport as interfaces + user-defined type guards, so both dimensions remain reachable.
Proposal
Class axis is semantic.
A2AErroris the shared base; each spec-aligned error extends it; each transport publishes a concrete per-transport subclass of every semantic error. Transport-native context is expressed as an interface implemented by all subclasses of a given transport, and narrowed at catch time via user-defined type guards (isRestError,isGrpcError,isJsonRpcError).flowchart TD Error --> A2AError A2AError --> TNF[TaskNotFoundError] A2AError --> UOE[UnsupportedOperationError] A2AError --> Etc["…8 more semantic errors"] TNF --> RTNF[RestTaskNotFoundError] TNF --> GTNF[GrpcTaskNotFoundError] TNF --> JTNF[JsonRpcTaskNotFoundError] RE[[RestA2AError<br/><i>interface</i>]] -.implements.-> RTNF GE[[GrpcA2AError<br/><i>interface</i>]] -.implements.-> GTNF JE[[JsonRpcA2AError<br/><i>interface</i>]] -.implements.-> JTNFA2AError— extendsError; carries spec-aligned reason/code and structured metadata. Base for all semantic errors.TaskNotFoundError,UnsupportedOperationError,TaskNotCancelableError,ContentTypeNotSupportedError,InvalidAgentResponseError,PushNotificationNotSupportedError,ExtendedAgentCardNotConfiguredError,VersionNotSupportedError,RequestMalformedError,ExtensionSupportRequiredError) — extendA2AError; carry error-specific fields (e.g.,taskId).RestTaskNotFoundError,GrpcTaskNotFoundError,JsonRpcTaskNotFoundError, and analogues for every semantic error) — extend the corresponding semantic class and implement the transport interface.RestA2AError,GrpcA2AError,JsonRpcA2AError) — declare transport-native context:RestA2AError:statusCode,headers,causeGrpcA2AError:status,statusDetailsBin(nocauseon the wire)JsonRpcA2AError:envelopeCode,dataisRestError(e),isGrpcError(e),isJsonRpcError(e)) — user-defined type predicates that narrow anA2AErrorto the transport interface.Catch-site ergonomics
The TS compiler intersects the class type with the interface type inside the guard, so both semantic fields (
taskId) and transport fields (statusCode) are simultaneously accessible.Closes #317.