Error Handling
July 14, 2026 · View on GitHub
This document describes ASAP error taxonomy and handling patterns.
Error Taxonomy
ASAP defines structured errors in asap.errors with a stable error code format:
asap:<domain>/<error>
Examples:
asap:protocol/invalid_stateasap:protocol/malformed_envelopeasap:task/not_found
Core Error Types
ASAPError: base class for all protocol errorsInvalidTransitionError: invalid task state transitionsMalformedEnvelopeError: invalid envelope payloadsTaskNotFoundError: task lookup failuresTaskAlreadyCompletedError: attempted updates to terminal tasks
ProtocolCorrelationError
Client-side fatal error (asap.transport.errors.ProtocolCorrelationError) raised
when a peer returns a well-formed envelope whose correlation_id does not
bind to the in-flight request envelope id. Taxonomy code:
asap:protocol/malformed_envelope.
| Transport path | Bound payload types |
|---|---|
Unary send() / batch() / WS recv | TaskResponse, McpToolResult, McpResourceData |
| SSE / WebSocket streaming | Above plus TaskStream |
from asap.transport.errors import ProtocolCorrelationError
try:
async for chunk in client.stream(request_envelope):
...
except ProtocolCorrelationError as exc:
# exc.request_id — expected request envelope id
# exc.correlation_id — value on the bad chunk
...
Server stream handlers must set correlation_id = request_envelope.id on every
chunk. See Transport — streaming correlation.
Usage Example
from asap.errors import InvalidTransitionError
try:
raise InvalidTransitionError(from_state="submitted", to_state="completed")
except InvalidTransitionError as exc:
error_payload = exc.to_dict()
# error_payload contains code, message, and details
JSON-RPC Mapping
Transport layer errors are surfaced as JSON-RPC error responses:
INVALID_REQUESTfor malformed JSON-RPC requestsINVALID_PARAMSfor invalid envelope payloadsMETHOD_NOT_FOUNDfor unknown payload typesINTERNAL_ERRORfor unexpected exceptions
Use structured logs with trace_id and correlation_id to debug failures across
agent boundaries.
Connection Error Troubleshooting
Connection errors are common when communicating with remote ASAP agents. This section provides guidance on diagnosing and resolving connection issues.
Common Connection Errors
ASAPConnectionError
Raised when the HTTP connection cannot be established or when the remote server returns an HTTP error status.
Error Message Format:
Connection failed to {url}. Verify the agent is running and accessible.
Troubleshooting: Check the URL format, network connectivity, and firewall settings.
Common Causes:
- Agent Not Running: The target agent service is not started or has crashed
- Incorrect URL: The base URL is malformed or points to the wrong endpoint
- Network Issues: Firewall blocking, DNS resolution failures, or network unreachable
- Port Mismatch: The agent is running on a different port than specified
- HTTPS/HTTP Mismatch: Using HTTP when HTTPS is required (or vice versa)
Diagnostic Steps:
from asap.transport.client import ASAPClient
from asap.errors import ASAPConnectionError
async def diagnose_connection(base_url: str):
"""Diagnose connection issues with an ASAP agent."""
try:
async with ASAPClient(base_url=base_url) as client:
# Try to validate connection
is_valid = await client._validate_connection()
if is_valid:
print(f"✓ Connection to {base_url} is valid")
else:
print(f"✗ Connection validation failed for {base_url}")
except ASAPConnectionError as e:
print(f"Connection Error: {e.message}")
print(f"URL: {e.url}")
print("\nTroubleshooting steps:")
print("1. Verify the agent is running:")
print(f" curl {base_url}/.well-known/asap/manifest.json")
print("2. Check URL format (should be http:// or https://)")
print("3. Verify network connectivity:")
print(f" ping {base_url.split('://')[1].split('/')[0]}")
print("4. Check firewall settings")
print("5. Verify port is correct")
ASAPTimeoutError
Raised when the HTTP request exceeds the configured timeout duration.
Error Message Format:
Request to {url} timed out after {timeout} seconds
Common Causes:
- Slow Network: High latency or slow network connection
- Server Overload: Server is processing requests slowly
- Timeout Too Short: Configured timeout is insufficient for the operation
- Large Payload: Request/response payload is large and takes time to transfer
Solutions:
# Increase timeout for slow connections
async with ASAPClient(
base_url="https://api.example.com",
timeout=120.0 # 2 minutes instead of default 60 seconds
) as client:
response = await client.send(envelope)
# For large payloads, consider:
# - Splitting into smaller requests
# - Using streaming for large responses
# - Increasing both client and server timeouts
CircuitOpenError
Raised when the circuit breaker is open and requests are rejected immediately.
Error Message Format:
Circuit breaker is OPEN for {base_url}. Too many consecutive failures ({count}).
Service temporarily unavailable.
Common Causes:
- Service Down: The remote service is completely unavailable
- High Failure Rate: Multiple consecutive failures have occurred
- Network Partition: Network connectivity issues causing repeated failures
Solutions:
from asap.errors import CircuitOpenError
try:
async with ASAPClient(
base_url="https://api.example.com",
circuit_breaker_enabled=True
) as client:
response = await client.send(envelope)
except CircuitOpenError as e:
print(f"Circuit is open: {e.message}")
print(f"Consecutive failures: {e.consecutive_failures}")
print("\nSolutions:")
print("1. Wait for circuit breaker timeout (default: 60s)")
print("2. Check if remote service is operational")
print("3. Verify network connectivity")
print("4. Consider disabling circuit breaker if failures are expected")
Diagnostic Checklist
When experiencing connection errors, follow this checklist:
1. Verify Agent is Running
# Check if agent is accessible
curl -I https://api.example.com/.well-known/asap/manifest.json
# Expected: HTTP/1.1 200 OK
# If 404: Agent may not be running or URL is incorrect
# If connection refused: Agent is not listening on that port
2. Check URL Format
# Valid URLs
"https://api.example.com" # ✓ HTTPS production
"http://localhost:8000" # ✓ HTTP localhost (development)
"https://localhost:8443" # ✓ HTTPS localhost
# Invalid URLs
"api.example.com" # ✗ Missing scheme
"ftp://api.example.com" # ✗ Unsupported scheme
"http://api.example.com" # ✗ HTTP in production (if require_https=True)
3. Test Network Connectivity
# Test DNS resolution
nslookup api.example.com
# Test TCP connection
telnet api.example.com 443
# Test HTTP connection
curl -v https://api.example.com/.well-known/asap/manifest.json
4. Verify Firewall and Security Groups
- Outbound Rules: Ensure your client can make outbound HTTPS connections
- Inbound Rules: Ensure the agent server accepts inbound connections on the configured port
- Security Groups: Check cloud provider security group rules (AWS, GCP, Azure)
5. Check SSL/TLS Configuration
# For development with self-signed certificates
async with ASAPClient(
base_url="https://localhost:8443",
verify_ssl=False # ⚠️ Development only, not for production
) as client:
response = await client.send(envelope)
6. Review Error Logs
The ASAP client provides structured logging with context:
import logging
from asap.observability import get_logger
logger = get_logger(__name__)
# Logs include:
# - target_url: The URL being accessed
# - attempt: Retry attempt number
# - status_code: HTTP status code (if available)
# - error: Error message
# - delay_seconds: Backoff delay before retry
Best Practices for Error Handling
1. Implement Retry Logic
The client includes automatic retry with exponential backoff, but you can add custom retry logic:
from asap.transport.client import ASAPClient
from asap.errors import ASAPConnectionError, ASAPTimeoutError
import asyncio
async def send_with_custom_retry(envelope, max_attempts=3):
"""Send envelope with custom retry logic."""
for attempt in range(max_attempts):
try:
async with ASAPClient(base_url="https://api.example.com") as client:
return await client.send(envelope)
except (ASAPConnectionError, ASAPTimeoutError) as e:
if attempt == max_attempts - 1:
raise # Last attempt failed
wait_time = 2 ** attempt # Exponential backoff
print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s...")
await asyncio.sleep(wait_time)
2. Handle Circuit Breaker Gracefully
from asap.errors import CircuitOpenError
try:
response = await client.send(envelope)
except CircuitOpenError:
# Circuit is open, use fallback or queue for later
print("Service unavailable, using fallback")
return fallback_response()
3. Validate Connection Before Sending
async with ASAPClient(base_url="https://api.example.com") as client:
# Optional: Validate connection before sending
is_valid = await client._validate_connection()
if not is_valid:
print("Connection validation failed, but attempting anyway...")
# Send envelope (will retry automatically on failure)
response = await client.send(envelope)
4. Monitor and Alert
Set up monitoring for connection errors:
from asap.errors import ASAPConnectionError
import metrics
async def monitored_send(client, envelope):
"""Send envelope with error monitoring."""
try:
response = await client.send(envelope)
metrics.increment("asap.requests.success")
return response
except ASAPConnectionError as e:
metrics.increment("asap.requests.connection_error")
metrics.increment(f"asap.errors.connection.{e.url}")
# Send alert if error rate is high
raise
Example: Complete Error Handling
from asap.transport.client import ASAPClient
from asap.errors import (
ASAPConnectionError,
ASAPTimeoutError,
CircuitOpenError,
ASAPRemoteError,
)
from asap.models.envelope import Envelope
async def robust_send(envelope: Envelope, base_url: str):
"""Send envelope with comprehensive error handling."""
try:
async with ASAPClient(
base_url=base_url,
timeout=30.0,
max_retries=3,
circuit_breaker_enabled=True,
) as client:
response = await client.send(envelope)
return response
except CircuitOpenError as e:
print(f"Circuit breaker is open: {e.message}")
print("Service is temporarily unavailable. Please try again later.")
raise
except ASAPConnectionError as e:
print(f"Connection failed: {e.message}")
print(f"Troubleshooting:")
print(f"1. Verify agent is running at {e.url}")
print(f"2. Check network connectivity")
print(f"3. Verify URL format and port")
raise
except ASAPTimeoutError as e:
print(f"Request timed out after {e.timeout} seconds")
print("Consider increasing timeout or checking network latency")
raise
except ASAPRemoteError as e:
print(f"Remote error: {e.code} - {e.message}")
print(f"Error details: {e.data}")
raise
Related Documentation
- Transport Guide - HTTP/JSON-RPC binding details
- Security Guide - Authentication and TLS configuration
- Observability - Logging and tracing for debugging