# How to Debug Common headroom.js Issues: A Complete Troubleshooting Guide

> Debug common headroom.js issues with this guide. Learn to troubleshoot proxy, client mode, and header handling problems using logging and validation tools.

- Repository: [Tejas Chopra/headroom](https://github.com/chopratejas/headroom)
- Tags: how-to-guide
- Published: 2026-06-17

---

**Most headroom.js runtime problems stem from three layers—proxy server misconfiguration, client mode settings, or content-specific header handling—and can be diagnosed using debug logging, environment variables, and the built-in `validate_setup()` helper.**

The [`headroom.js`](https://github.com/chopratejas/headroom/blob/main/headroom.js) JavaScript client (distributed as the `headroom-ai` npm package) wraps the same compression pipeline that powers the Python and Rust implementations in the **chopratejas/headroom** repository. When token savings disappear, proxies refuse connections, or beta headers persist unexpectedly, systematic debugging of these three layers quickly isolates the root cause.

## Understanding the Three Layers of headroom.js Debugging

Headroom’s architecture separates concerns into distinct layers. Identifying which layer produces the symptom accelerates your fix.

### Proxy and Server Issues

Symptoms manifest as `headroom proxy` startup failures, "connection refused" errors, or HTTP 502/503 responses.

**Diagnosis steps:**

1. Launch the proxy with verbose logging: `headroom proxy --log-level debug`
2. Check for port-conflict messages (e.g., "addr already in use")
3. Verify required environment variables like `OPENAI_API_KEY` are exported

**Resolution:**
- Change the listening port using `--port 8788` if the default is occupied
- Install missing extras: `npm install headroom-ai` or `pip install "headroom-ai[proxy]"`
- Restart the proxy to clear in-memory trackers (see Session Beta Header Tracking below)

### Client Configuration Problems

When you observe zero token savings, overly aggressive compression, or high latency, the client configuration is typically misaligned.

**Diagnosis steps:**
- Inspect the runtime mode: `client.get_stats()['config']['mode']`
- Check which transforms are enabled via `smart_crusher_enabled`
- Measure timing by setting `logging.basicConfig(level=logging.DEBUG)`

**Resolution:**
- Set mode to `"optimize"` (the default is `"audit"`, which performs no compression)
- Adjust `smart_crusher` thresholds: modify `min_tokens_to_crush` and `max_items_after_crush`
- Disable unused transforms: `cache_aligner.enabled = false`

### Content-Specific Bugs

Missing tool output, validation errors on setup, or unexpected header injection indicate content-layer issues.

**Diagnosis steps:**
- Enable beta-header sticky debugging: `HEADROOM_BETA_HEADER_STICKY=debug`
- Run the `validate_setup()` helper to surface schema errors
- Inspect request/response logs for missing `anthropic-beta` or custom headers

**Resolution:**
- Turn off header-sticky behavior: `HEADROOM_BETA_HEADER_STICKY=disabled`
- Re-run `validate_setup()` and fix configuration mismatches
- Pass `headroom_tool_profiles` for tools that must not be compressed

## Step-by-Step Debugging Workflow for JavaScript

Execute this terminal workflow to isolate issues systematically.

Start the proxy in debug mode and verify health:

```bash

# Start proxy with detailed logging

headroom proxy --log-level debug &
PROXY_PID=$!

# Verify proxy is listening (adjust port if using --port)

curl -s http://localhost:8787/health && echo "✅ proxy up"

```

Run a minimal JavaScript test with explicit configuration:

```javascript
import { HeadroomClient, OpenAIProvider } from 'headroom-ai';
import { OpenAI } from 'openai';

const client = new HeadroomClient({
  originalClient: new OpenAI({ apiKey: process.env.OPENAI_API_KEY }),
  provider: new OpenAIProvider(),
  defaultMode: 'optimize',           // Ensure compression is active
});

(async () => {
  const resp = await client.chat.completions.create({
    model: 'gpt-4o',
    messages: [{ role: 'user', content: 'Hello' }],
  });
  console.log('✅ response', resp.choices[0].message.content);
  console.log('Stats', client.getStats());
})();

```

**If `tokens_saved_total` reports zero:**
- Confirm mode is `"optimize"` (not `"audit"`)
- Check `smart_crusher.min_tokens_to_crush` (default is 200 tokens)
- Verify the request contains tool output (compression only runs on tool data)

**If requests fail with 502/503:**
Inspect the proxy's debug log for stack traces:

```bash
cat $(headroom proxy --log-dir)/headroom.log | grep -iE 'error|stack|smartcrusher'

```

## Common headroom.js Issues and Their Fixes

| Symptom | Root Cause | Fix |
|---------|------------|-----|
| **Proxy hangs on startup** | Port already bound by stale process | Run `lsof -i :8787` to find the PID, kill it, or start on a different port (`--port 8788`). |
| **"Connection refused" errors** | Proxy not running or wrong port | Run `headroom proxy --log-level debug` and verify `curl http://localhost:8787/health`. |
| **Beta-header token persists** | `SessionBetaTracker` re-injects old `anthropic-beta` values | Set `HEADROOM_BETA_HEADER_STICKY=disabled` or restart the proxy to clear the tracker. |
| **Zero token savings** | Client in `"audit"` mode or `smart_crusher` disabled/threshold too high | Set `defaultMode: "optimize"` and lower `smart_crusher.min_tokens_to_crush`. |
| **Missing information in LLM response** | Compression too aggressive (`max_items_after_crush=15`) | Increase `smart_crusher.max_items_after_crush` (e.g., to 50) or disable compression for that tool via `headroom_tool_profiles`. |
| **High latency** | Embedding-based relevance scoring or low thresholds | Switch to BM25 (`config.smart_crusher.relevance.tier = "bm25"`), raise the token threshold, or disable unused transforms (`cache_aligner.enabled = false`). |

## Navigating the Source Code

When debugging requires source-level inspection, refer to these specific files in the **chopratejas/headroom** repository:

- **[`sdk/typescript/src/client.ts`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/client.ts)** — Exposes `HeadroomClient`, wraps LLM providers, and handles request-level mode toggles via `defaultMode`.
- **[`sdk/typescript/src/errors.ts`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/errors.ts)** — Defines custom error types that surface in JavaScript stack traces.
- **[`sdk/typescript/src/types/config.ts`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/types/config.ts)** — Shapes the `HeadroomConfig` object used by the client (e.g., `smart_crusher` thresholds).
- **[`sdk/typescript/src/utils/format.ts`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/utils/format.ts)** — Formats diagnostic messages when `--log-level debug` is set.
- **[`crates/headroom-proxy/src/handlers/mod.rs`](https://github.com/chopratejas/headroom/blob/main/crates/headroom-proxy/src/handlers/mod.rs)** — Core Rust proxy handlers exposing `/debug/*` endpoints used by the JavaScript client.
- **[`wiki/troubleshooting.md`](https://github.com/chopratejas/headroom/blob/main/wiki/troubleshooting.md)** — Canonical source of diagnostic steps.

## Summary

- **Start with proxy debug logs:** Running `headroom proxy --log-level debug` reveals exactly where compression or routing decisions fail.
- **Verify client mode and transforms:** The JavaScript SDK defaults to `"audit"` mode (no savings) and aggressive compression settings that may require tuning.
- **Use `validate_setup()`:** This built-in helper catches misconfigured API keys and malformed profiles before runtime.
- **Check header handling:** Set `HEADROOM_BETA_HEADER_STICKY=disabled` when encountering persistent beta-header issues.

When these checks pass, the proxy's `/debug/*` endpoints (available only from `127.0.0.1`) provide snapshots of internal state for deeper investigation.

## Frequently Asked Questions

### Why is headroom.js showing zero token savings?

The JavaScript client defaults to `"audit"` mode, which calculates savings without actually compressing requests. Set `defaultMode: "optimize"` in your `HeadroomClient` constructor, and ensure your request contains tool output (the compressor only processes tool data). Also verify that `smart_crusher.min_tokens_to_crush` (default 200) is not set higher than your actual token count.

### How do I fix "connection refused" when starting the headroom proxy?

This error indicates the proxy is not running or is listening on a different port. Run `headroom proxy --log-level debug` to confirm startup, then verify with `curl http://localhost:8787/health`. If the port is occupied by a stale process, use `lsof -i :8787` to identify and kill the process, or specify an alternative port with `--port 8788`.

### What causes persistent anthropic-beta headers in my requests?

The `SessionBetaTracker` in the proxy re-injects cached `anthropic-beta` values across sessions. To disable this behavior, set the environment variable `HEADROOM_BETA_HEADER_STICKY=disabled`. Alternatively, restart the proxy to clear the in-memory tracker state.

### How can I reduce latency when using headroom.js?

High latency typically stems from embedding-based relevance scoring or excessively low compression thresholds. Switch to BM25 via `config.smart_crusher.relevance.tier = "bm25"`, raise `min_tokens_to_crush`, or disable unused transforms like `cache_aligner` by setting `cache_aligner.enabled = false`.