diff --git a/fern/01-guide/02-languages/rest-error-handling.mdx b/fern/01-guide/02-languages/rest-error-handling.mdx
new file mode 100644
index 0000000000..5c70d814f6
--- /dev/null
+++ b/fern/01-guide/02-languages/rest-error-handling.mdx
@@ -0,0 +1,416 @@
+---
+title: "HTTP API Error Handling"
+description: "Understanding and handling errors from the BAML HTTP API"
+---
+
+The BAML HTTP API (also known as the REST API) provides structured error responses to help you handle different failure scenarios when calling BAML functions over HTTP.
+
+## Error Response Format
+
+All BAML HTTP API errors return JSON responses with a consistent structure:
+
+```json
+{
+ "error": "error_type",
+ "message": "Human-readable error description",
+ "documentation_url": "https://docs.boundaryml.com/get-started/debugging/exception-handling"
+}
+```
+
+Additional fields may be present depending on the specific error type.
+
+## HTTP Status Codes
+
+The BAML HTTP API uses standard HTTP status codes to indicate the outcome of requests:
+
+| Status Code | Meaning | When Used |
+|-------------|---------|-----------|
+| `200` | OK | Request succeeded |
+| `400` | Bad Request | Invalid request format or arguments |
+| `403` | Forbidden | Authentication failed |
+| `500` | Internal Server Error | Server-side processing errors |
+| `502` | Bad Gateway | LLM client errors |
+| `XXX` | Variable | HTTP errors from LLM providers (preserves original status) |
+
+## Error Types
+
+### `invalid_argument`
+
+**HTTP Status:** `400 Bad Request`
+
+Returned when the request contains invalid arguments or malformed data.
+
+```json
+{
+ "error": "invalid_argument",
+ "message": "POST data must be valid JSON: expected value at line 1 column 1"
+}
+```
+
+**Common causes:**
+- Malformed JSON in request body
+- Missing required function arguments
+- Arguments that don't match expected types
+- Invalid `__baml_options__` format
+
+**Example:**
+```bash
+curl -X POST http://localhost:2024/call/ExtractName \
+ -H "Content-Type: application/json" \
+ -d '{"invalid_json": }'
+```
+
+### `client_error`
+
+**HTTP Status:** `502 Bad Gateway`
+
+Returned when the LLM client fails to return a valid response.
+
+```json
+{
+ "error": "client_error",
+ "message": "LLMFailure(ErrorCode::RateLimited, \"Rate limit exceeded\")"
+}
+```
+
+**Common causes:**
+- LLM provider rate limiting
+- Invalid API credentials
+- LLM provider service unavailable
+- Network timeouts
+- Unsupported model parameters
+
+**Example scenarios:**
+- OpenAI returns 429 (rate limited)
+- Anthropic returns 401 (invalid API key)
+- Provider returns 503 (service unavailable)
+
+### `client_http_error`
+
+**HTTP Status:** Matches the original LLM provider's status code
+
+Returned when an HTTP request to an LLM provider fails with a non-200 status code.
+
+```json
+{
+ "error": "client_http_error",
+ "message": "HTTP 429: Rate limit exceeded",
+ "client_name": "gpt_4o",
+ "status_code": 429
+}
+```
+
+**Additional fields:**
+- `client_name`: Name of the BAML client that failed
+- `status_code`: Original HTTP status code from the provider
+
+### `validation_failure`
+
+**HTTP Status:** `500 Internal Server Error`
+
+Returned when BAML successfully receives a response from the LLM but fails to parse it into the expected output format.
+
+```json
+{
+ "error": "validation_failure",
+ "message": "Failed to parse response into Person schema",
+ "prompt": "Extract the person's name from: John Doe is 30 years old",
+ "raw_output": "The person is named John"
+}
+```
+
+**Additional fields:**
+- `prompt`: The original prompt sent to the LLM
+- `raw_output`: The raw text response from the LLM
+
+**Common causes:**
+- LLM returns unstructured text instead of expected JSON
+- LLM output doesn't match the defined schema
+- Parsing errors due to malformed LLM responses
+
+### `finish_reason_error`
+
+**HTTP Status:** `500 Internal Server Error`
+
+Returned when the LLM terminates with a disallowed finish reason (e.g., length limit reached when only "stop" is allowed).
+
+```json
+{
+ "error": "finish_reason_error",
+ "message": "LLM finished with reason 'length' but only 'stop' is allowed",
+ "prompt": "Write a long story about...",
+ "raw_output": "Once upon a time there was a",
+ "finish_reason": "length"
+}
+```
+
+**Additional fields:**
+- `prompt`: The original prompt sent to the LLM
+- `raw_output`: The partial response received before termination
+- `finish_reason`: The actual finish reason that caused the error
+
+### `internal_error`
+
+**HTTP Status:** `500 Internal Server Error`
+
+Returned for unexpected server-side errors that don't fit other categories.
+
+```json
+{
+ "error": "internal_error",
+ "message": "Unexpected error during function execution: connection timeout"
+}
+```
+
+**Common causes:**
+- Server configuration issues
+- Unexpected runtime exceptions
+- Resource exhaustion
+- Database connectivity issues
+
+## Authentication Errors
+
+When authentication is enabled via `BAML_PASSWORD`, authentication failures return:
+
+**HTTP Status:** `403 Forbidden`
+
+```json
+{
+ "authz": {
+ "enforcement": "active",
+ "outcome": "fail",
+ "reason": "Incorrect x-baml-api-key"
+ }
+}
+```
+
+**Authentication methods:**
+1. **API Key Header (Recommended):**
+ ```bash
+ curl -H "x-baml-api-key: your-api-key" \
+ http://localhost:2024/call/MyFunction
+ ```
+
+2. **Basic Authentication:**
+ ```bash
+ curl -u "username:your-api-key" \
+ http://localhost:2024/call/MyFunction
+ ```
+
+## Error Handling Best Practices
+
+### 1. Handle Specific Error Types
+
+```python
+import requests
+import json
+
+def call_baml_function(function_name, args):
+ try:
+ response = requests.post(
+ f"http://localhost:2024/call/{function_name}",
+ json=args,
+ headers={"x-baml-api-key": "your-key"}
+ )
+
+ if response.status_code == 200:
+ return response.json()
+
+ error_data = response.json()
+ error_type = error_data.get("error")
+
+ if error_type == "invalid_argument":
+ # Handle bad request - fix arguments and retry
+ print(f"Invalid arguments: {error_data['message']}")
+ return None
+
+ elif error_type == "client_error":
+ # Handle LLM client errors - possibly retry with backoff
+ print(f"LLM client error: {error_data['message']}")
+ return None
+
+ elif error_type == "validation_failure":
+ # Handle parsing errors - maybe use a fixup function
+ print(f"Validation failed: {error_data['message']}")
+ print(f"Raw output: {error_data['raw_output']}")
+ return None
+
+ elif error_type == "client_http_error":
+ status_code = error_data.get("status_code", 0)
+ if status_code == 429:
+ # Rate limited - implement backoff
+ print("Rate limited, waiting before retry...")
+ return None
+ else:
+ print(f"HTTP error {status_code}: {error_data['message']}")
+ return None
+
+ else:
+ # Handle unknown errors
+ print(f"Unknown error: {error_data}")
+ return None
+
+ except requests.exceptions.RequestException as e:
+ print(f"Network error: {e}")
+ return None
+ except json.JSONDecodeError:
+ print("Invalid JSON response")
+ return None
+```
+
+### 2. Implement Retry Logic
+
+```python
+import time
+import random
+
+def call_with_retry(function_name, args, max_retries=3):
+ for attempt in range(max_retries):
+ try:
+ response = requests.post(
+ f"http://localhost:2024/call/{function_name}",
+ json=args,
+ timeout=30
+ )
+
+ if response.status_code == 200:
+ return response.json()
+
+ error_data = response.json()
+ error_type = error_data.get("error")
+
+ # Don't retry client errors (bad arguments)
+ if error_type == "invalid_argument":
+ raise ValueError(f"Invalid arguments: {error_data['message']}")
+
+ # Retry server errors and rate limits
+ if error_type in ["client_error", "client_http_error", "internal_error"]:
+ if attempt < max_retries - 1:
+ # Exponential backoff with jitter
+ delay = (2 ** attempt) + random.uniform(0, 1)
+ time.sleep(delay)
+ continue
+
+ # Don't retry validation failures
+ raise Exception(f"BAML error: {error_data}")
+
+ except requests.exceptions.Timeout:
+ if attempt < max_retries - 1:
+ time.sleep(2 ** attempt)
+ continue
+ raise
+
+ raise Exception(f"Failed after {max_retries} attempts")
+```
+
+### 3. Handle Validation Failures with Fixup
+
+When you receive a `validation_failure`, you can use a fixup function to correct the LLM output:
+
+```python
+def call_with_fixup(function_name, args):
+ try:
+ # Try the main function
+ response = requests.post(
+ f"http://localhost:2024/call/{function_name}",
+ json=args
+ )
+
+ if response.status_code == 200:
+ return response.json()
+
+ error_data = response.json()
+
+ # If validation failed, try fixup function
+ if error_data.get("error") == "validation_failure":
+ fixup_args = {
+ "error_message": error_data["message"],
+ "raw_output": error_data["raw_output"],
+ "expected_format": "Person with name and age fields"
+ }
+
+ fixup_response = requests.post(
+ f"http://localhost:2024/call/Fixup{function_name}",
+ json=fixup_args
+ )
+
+ if fixup_response.status_code == 200:
+ return fixup_response.json()
+
+ # Handle other errors normally
+ raise Exception(f"BAML error: {error_data}")
+
+ except Exception as e:
+ print(f"Error calling BAML function: {e}")
+ return None
+```
+
+## Debugging Errors
+
+### 1. Use Debug Endpoints
+
+Check server status and authentication:
+```bash
+curl http://localhost:2024/_debug/status
+```
+
+Test connectivity:
+```bash
+curl http://localhost:2024/_debug/ping
+```
+
+### 2. Enable Detailed Logging
+
+Set environment variable for verbose logging:
+```bash
+export BAML_LOG=trace
+baml-cli serve --port 2024
+```
+
+### 3. Use Interactive Documentation
+
+Visit `http://localhost:2024/docs` to test functions interactively and see detailed error responses.
+
+## Streaming Errors
+
+When using the streaming endpoint (`POST /stream/:function_name`), errors are returned as Server-Sent Events:
+
+```
+data: {"error": "validation_failure", "message": "Failed to parse", ...}
+```
+
+Handle streaming errors in your client:
+
+```javascript
+const eventSource = new EventSource('/stream/MyFunction');
+
+eventSource.onmessage = function(event) {
+ const data = JSON.parse(event.data);
+
+ if (data.error) {
+ console.error('Streaming error:', data);
+ eventSource.close();
+ return;
+ }
+
+ // Handle successful data
+ console.log('Received:', data);
+};
+
+eventSource.onerror = function(event) {
+ console.error('EventSource failed:', event);
+};
+```
+
+## Error Reference Summary
+
+| Error Type | Status | Retry? | Common Cause |
+|------------|---------|---------|--------------|
+| `invalid_argument` | 400 | No | Bad request format |
+| `client_error` | 502 | Yes | LLM provider issues |
+| `client_http_error` | Variable | Depends | HTTP errors from providers |
+| `validation_failure` | 500 | Fixup | LLM output parsing failed |
+| `finish_reason_error` | 500 | No | LLM terminated unexpectedly |
+| `internal_error` | 500 | Yes | Server-side issues |
+
+For more information about BAML error handling in general, see the [Error Handling Guide](/guide/baml-basics/error-handling).
\ No newline at end of file
diff --git a/fern/01-guide/02-languages/rest.mdx b/fern/01-guide/02-languages/rest.mdx
index 5db57d1f3f..c6941446eb 100644
--- a/fern/01-guide/02-languages/rest.mdx
+++ b/fern/01-guide/02-languages/rest.mdx
@@ -218,6 +218,10 @@ We integrate with [OpenAPI](https://www.openapis.org/) (universal API definition
[`baml-examples`](https://github.com/BoundaryML/baml-examples) for example
projects with instructions for running them.
+
+ For comprehensive error handling information, including HTTP status codes, error types, and best practices, see the [HTTP API Error Handling Guide](/guide/languages/rest-error-handling).
+
+
We've tested the below listed OpenAPI clients, but not all of them. If you run
into issues with any of the OpenAPI clients, please let us know, either in
diff --git a/fern/03-reference/baml-cli/serve.mdx b/fern/03-reference/baml-cli/serve.mdx
index f674fee1fa..3e8ac5fd7a 100644
--- a/fern/03-reference/baml-cli/serve.mdx
+++ b/fern/03-reference/baml-cli/serve.mdx
@@ -79,6 +79,10 @@ We support the header: `x-baml-api-key`
Set the `BAML_PASSWORD` environment variable to enable authentication.
+## Error Handling
+
+The BAML HTTP API returns structured JSON error responses with appropriate HTTP status codes. For comprehensive information about error types, status codes, and handling strategies, see the [HTTP API Error Handling Guide](/guide/languages/rest-error-handling).
+
## Examples
1. Start the server with default settings:
diff --git a/fern/docs.yml b/fern/docs.yml
index 9a54821116..f72e045971 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -318,9 +318,14 @@ navigation:
- page: Ruby
icon: fa-regular fa-gem
path: 01-guide/02-languages/ruby.mdx
- - page: REST API (other languages)
+ - section: REST API (other languages)
icon: fa-regular fa-network-wired
- path: 01-guide/02-languages/rest.mdx
+ contents:
+ - page: Getting Started
+ path: 01-guide/02-languages/rest.mdx
+ - page: Error Handling
+ icon: fa-solid fa-exclamation-triangle
+ path: 01-guide/02-languages/rest-error-handling.mdx
- page: Elixir
icon: fa-brands fa-erlang
path: 01-guide/02-languages/elixir.mdx