scripts/provider-error-proxy/README.md
A network-level HTTP proxy for simulating provider errors when testing Goose's error handling and retry logic.
# 1. Start the proxy (from scripts/provider-error-proxy directory)
uv run proxy.py
# 2. In another terminal, configure Goose to use the proxy
export OPENAI_HOST=http://localhost:8888
export ANTHROPIC_HOST=http://localhost:8888
export GOOGLE_HOST=http://localhost:8888
export OPENROUTER_HOST=http://localhost:8888
export TETRATE_HOST=http://localhost:8888
export DATABRICKS_HOST=http://localhost:8888
# For Databricks with OAuth, also set the real host:
export DATABRICKS_REAL_HOST=https://your-workspace.databricks.com
# 3. Run Goose normally
goose session start "tell me a joke"
# 4. In the proxy terminal, use interactive commands:
# n - No error (pass through) - permanent
# c - Context length exceeded error
# r - Rate limit error
# u - Unknown server error (500)
# q - Quit
This project uses uv for Python dependency management. From the scripts/provider-error-proxy directory:
# Install dependencies (uv will handle this automatically)
uv sync
Start the proxy with default settings (port 8888):
uv run proxy.py
Use a custom port:
uv run proxy.py --port 9000
Start the proxy with an initial error mode (for automated testing):
# Start with context length error (3 times)
uv run proxy.py --mode "c 3"
# Start with rate limit error (30% of requests)
uv run proxy.py --mode "r 30%"
# Start with server error (all requests)
uv run proxy.py --mode "u *"
Command-line options:
--port PORT - Port to listen on (default: 8888)--mode COMMAND - Initial error mode command (e.g., "c 3", "r 30%", "u *", "n")
--no-stdin - Disable stdin reader (for background/automated mode)For automated tests or background usage, combine --no-stdin with --mode:
# Run in background for automated testing
uv run proxy.py --mode "c 3" --no-stdin &
PROXY_PID=$!
# ... run your tests ...
# Stop the proxy
kill $PROXY_PID
Once the proxy is running, you can control error injection interactively:
n - No error (pass through all requests normally) - permanent modec - Context length exceeded error (1 time by default)
c 4 - Inject error 4 times in a rowc 0.3 or c 30% - Inject error on 30% of requestsc * - Inject error on 100% of requests (all requests fail)r - Rate limit error (1 time by default, same modifiers as c)u - Unknown server error (500) (1 time by default, same modifiers as c)q - Quit the proxyNote: Whitespace is flexible - c 100%, c100%, c *, and c* all work the same way.
The proxy will display the current mode and request count after each command.
Set environment variables to redirect provider traffic through the proxy:
export OPENAI_HOST=http://localhost:8888
export ANTHROPIC_HOST=http://localhost:8888
export GOOGLE_HOST=http://localhost:8888
export OPENROUTER_HOST=http://localhost:8888
export TETRATE_HOST=http://localhost:8888
export DATABRICKS_HOST=http://localhost:8888
For providers that require authentication or metadata endpoints (like Databricks with OAuth), you also need to set the real host:
export DATABRICKS_REAL_HOST=https://your-workspace.databricks.com
Then run Goose normally. The proxy will intercept API requests and you can manually trigger errors as needed, while authentication and metadata requests are forwarded to the real provider.
The proxy automatically detects and handles streaming responses by:
text/event-stream content type (Server-Sent Events)StreamResponse to forward chunks in real-time without bufferingThis means streaming completions from providers like OpenAI, Anthropic, and Databricks work seamlessly through the proxy.
The proxy returns realistic error responses for each provider:
c)context_length_exceeded errorINVALID_ARGUMENT statusINVALID_PARAMETER_VALUE errorr)rate_limit_exceeded errorrate_limit_error typeRESOURCE_EXHAUSTED statusRATE_LIMIT_EXCEEDED erroru)internal_server_error erroroverloaded_error typeUNAVAILABLE statusINTERNAL_ERROR error$ uv run proxy.py
============================================================
š§ Provider Error Proxy
============================================================
Port: 8888
To use with Goose, set these environment variables:
export OPENAI_HOST=http://localhost:8888
export ANTHROPIC_HOST=http://localhost:8888
...
============================================================
============================================================
Current mode: ā
No error (pass through)
Requests handled: 0
============================================================
Commands:
n - No error (pass through) - permanent
c - Context length exceeded (1 time)
c 4 - Context length exceeded (4 times)
c 0.3 - Context length exceeded (30% of requests)
c 30% - Context length exceeded (30% of requests)
c * - Context length exceeded (100% of requests)
r - Rate limit error (1 time)
u - Unknown server error (1 time)
q - Quit
Enter command: r
============================================================
Current mode: ā±ļø Rate limit exceeded (1 remaining)
Requests handled: 0
============================================================
...
2025-10-09 14:30:15 - __main__ - INFO - šØ Request #1: POST /v1/chat/completions -> openai
2025-10-09 14:30:15 - __main__ - WARNING - š„ Injecting RATE_LIMIT error (status 429) for openai
Enter command: n
============================================================
Current mode: ā
No error (pass through)
Requests handled: 1
============================================================
...
2025-10-09 14:30:20 - __main__ - INFO - šØ Request #2: POST /v1/chat/completions -> openai
2025-10-09 14:30:20 - __main__ - INFO - ā
Proxied response: 200
The proxy is built with aiohttp for async HTTP handling. Key components:
ErrorProxy: Main proxy class that handles request interception and error injectionErrorMode: Enum defining the available error injection modesdetect_provider(): Identifies which provider based on headers/pathshandle_request(): Main request handler that either proxies or returns errorsstdin_reader(): Thread that reads interactive commands from stdinTo test the proxy:
uv run proxy.py