# How FreeLLMAPI Handles Error Redaction and Classification to Sanitize Provider Responses

> Learn how FreeLLMAPI’s error redaction and classification process sanitizes provider responses, preventing sensitive data leaks and identifying retryable or payment-related failures.

- Repository: [Tashfeen/freellmapi](https://github.com/tashfeenahmed/freellmapi)
- Tags: internals
- Published: 2026-06-25

---

**FreeLLMAPI sanitizes raw provider errors through regex-based redaction that strips tokens and secrets, then classifies the sanitized error to determine if the failure is retryable, payment-related, or a model-access issue without exposing sensitive data to logs or clients.**

The tashfeenahmed/freellmapi repository implements a strict two-stage pipeline to handle failures from upstream LLM providers. First, it redacts sensitive material from error messages; second, it categorizes the error type to drive fallback logic. This ensures that API keys, bearer tokens, and URLs never leak into analytics or user-facing responses while enabling the system to make intelligent routing decisions.

## Error Redaction Pipeline

The redaction layer lives in [`server/src/lib/error-redaction.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-redaction.ts) and provides a single exported function that transforms any thrown value into a safe, truncated string.

### Regex Patterns for Sensitive Data Removal

The module defines a `REDACTIONS` array containing `[RegExp, string]` pairs that target specific secret patterns:

- **Bearer tokens**: `/\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi` → `Bearer [redacted]`
- **API keys and secrets**: `/\b(api[_-]?key|access[_-]?token|token|secret|authorization)(\s*[:=]\s*)(["']?)[^"',\s}\]]+/gi` → captures the key name and delimiter, replaces the value with `[redacted]`
- **OpenAI-style keys**: `/\bsk-[A-Za-z0-9_-]{8,}\b/g` → `[redacted-key]`
- **JWT-like tokens**: `/\b[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g` → `[redacted-token]`
- **URLs**: `/\bhttps?:\/\/[^\s"'<>)]*/gi` → `[redacted-url]` to prevent query-string credential leaks

### The sanitizeProviderErrorMessage Function

This utility processes raw errors through a strict normalization sequence:

1. Converts non-string inputs using `String(message ?? '')`
2. Trims whitespace and defaults to `"Provider error"` if empty
3. Iterates through the `REDACTIONS` array, applying each regex replacement
4. Collapses multiple spaces and trims again
5. Enforces the `MAX_PROVIDER_ERROR_LENGTH` constant (240 characters), appending `"…"` if truncated

```typescript
import { sanitizeProviderErrorMessage } from '../lib/error-redaction.js';

function handleProviderError(err: unknown) {
  const safe = sanitizeProviderErrorMessage(err);
  console.warn('Provider error (redacted):', safe);
}

```

## Error Classification System

After redaction, the system classifies the original error object to determine downstream handling logic. The pure utility functions reside in [`server/src/lib/error-classify.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-classify.ts).

### Detecting Retryable Failures

The `isRetryableError` function examines HTTP status codes and message substrings to decide if a request should be retried on an alternate provider. It returns true for:

- Status codes: `408`, `409`, `410`, `429`, or any `5xx` (server errors)
- Message content: `"rate limit"`, `"quota"`, `"timeout"`, `"503"`, `"404"` (when transient)

### Payment and Access Violations

Three additional functions identify specific failure modes:

- **`isPaymentRequiredError`**: Detects HTTP `402` or messages containing `"payment required"` or `"insufficient_quota"` (lines 71–78)
- **`isModelNotFoundError`**: Flags HTTP `404`/`410` or messages with `"not found"` or `"no endpoints found"` (lines 86–95)
- **`isModelAccessForbiddenError`**: Catches HTTP `403` or messages containing `"403"`/`"forbidden"` when a key is valid but the model tier is restricted (lines 100–107)

## Integration Across the Request Lifecycle

Both libraries are imported wherever provider responses are processed. In [`server/src/routes/proxy.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/proxy.ts), the functions are imported on lines 13 and 17, then invoked inside catch blocks:

```typescript
import { sanitizeProviderErrorMessage } from '../lib/error-redaction.js';
import {
  isRetryableError,
  isPaymentRequiredError,
  isModelNotFoundError,
  isModelAccessForbiddenError,
} from '../lib/error-classify.js';

async function proxyRequest(req, res) {
  try {
    // … upstream LLM call …
  } catch (err) {
    const safeMsg = sanitizeProviderErrorMessage(err);
    traceRouteEvent('Proxy', { error: safeMsg });

    if (isRetryableError(err)) {
      await retryWithNextProvider(req, res);
    } else if (isPaymentRequiredError(err)) {
      await benchKeyForPayment(req);
    } else if (isModelNotFoundError(err) || isModelAccessForbiddenError(err)) {
      await skipModel(req);
    } else {
      res.status(502).json({ error: { message: safeMsg } });
    }
  }
}

```

The same pattern appears in [`server/src/routes/responses.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/responses.ts) (line 31) and [`server/src/services/fusion.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/fusion.ts) (line 17), ensuring consistent sanitization and logic across the proxy, chat completions endpoint, and fusion panel.

## Summary

- **Redaction occurs first**: `sanitizeProviderErrorMessage` in [`server/src/lib/error-redaction.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-redaction.ts) strips bearer tokens, API keys, and URLs using curated regexes, then truncates to 240 characters.
- **Classification follows**: Pure functions in [`server/src/lib/error-classify.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-classify.ts) label errors as retryable, payment-required, model-not-found, or access-forbidden based on HTTP status codes and message substrings.
- **No data leakage**: Secrets never reach logs, analytics, or JSON responses because redaction happens before any inspection or transmission.
- **Consistent routing**: The proxy, responses route, and fusion service all share the same error-handling utilities, enabling centralized fallback and retry logic.

## Frequently Asked Questions

### What types of sensitive data does FreeLLMAPI redact from provider errors?

FreeLLMAPI redacts Bearer tokens, OpenAI-style secret keys (`sk-...`), JWT-like tokens, generic API keys and access tokens, and full URLs. These patterns are defined in the `REDACTIONS` array within [`server/src/lib/error-redaction.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/lib/error-redaction.ts).

### How does FreeLLMAPI determine if a provider error is retryable?

The `isRetryableError` function checks for HTTP status codes `408`, `409`, `410`, `429`, or any server error `≥ 500`. It also scans the error message for substrings like `"rate limit"`, `"quota"`, `"timeout"`, or `"503"`. If matched, the system routes the request to the next available provider.

### What is the maximum length of a sanitized error message in FreeLLMAPI?

The constant `MAX_PROVIDER_ERROR_LENGTH` caps sanitized messages at **240 characters**. Longer strings are truncated and appended with an ellipsis (`…`).

### Where does error classification occur in the FreeLLMAPI request pipeline?

Classification occurs in the catch blocks of provider-facing routes and services, specifically in [`server/src/routes/proxy.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/proxy.ts), [`server/src/routes/responses.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/routes/responses.ts), and [`server/src/services/fusion.ts`](https://github.com/tashfeenahmed/freellmapi/blob/main/server/src/services/fusion.ts). These files import the classification utilities to decide whether to retry, bench a key, skip a model, or return the sanitized error to the client.