That OpenAI Call From Your Browser Is Failing. Here's Why.

# ai# debugging# python
That OpenAI Call From Your Browser Is Failing. Here's Why.Tracepilot

That OpenAI Call From Your Browser Is Failing. Here's Why. You're fetching from...

That OpenAI Call From Your Browser Is Failing. Here's Why.

You're fetching from https://api.openai.com/v1/chat/completions directly in the browser.

It works in dev. Then production hits you with a CORS error. Or a 401. Or worse — a silent failure where the request goes out but never comes back.

Sound familiar?

Let's cut through the noise. Here's what's actually breaking.

The Problem

Browser JavaScript can't call OpenAI directly. OpenAI's API doesn't set Access-Control-Allow-Origin headers for browser origins. That's by design — they don't want API keys sitting in client-side code.

But you already know that. The issue is deeper.

When you do this:

// ❌ This will fail in production
const response = await fetch('https://api.openai.com/v1/chat/completions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${process.env.NEXT_PUBLIC_OPENAI_KEY}`
  },
  body: JSON.stringify({ model: 'gpt-4o-mini', messages: [...] })
});
Enter fullscreen mode Exit fullscreen mode

Three things happen:

  1. The browser sends a preflight OPTIONS request — OpenAI's server doesn't respond with the right CORS headers → blocked.
  2. Your API key is exposed — anyone can open DevTools and steal it.
  3. Rate limiting hits your client IP — every user shares the same rate limit bucket.

The fix isn't "just use a proxy." The fix is understanding why your specific setup is failing.

Why It Breaks (The Real Details)

OpenAI's API response headers include:

access-control-allow-origin: *
Enter fullscreen mode Exit fullscreen mode

Wait, that looks fine. So why does it fail?

Because the preflight request — the OPTIONS call the browser sends first — doesn't get past OpenAI's CDN. Cloudflare blocks it. The browser never sees the access-control-allow-origin header on the actual response because the preflight never completed.

Check your browser's network tab. You'll see:

OPTIONS https://api.openai.com/v1/chat/completions → 403
Enter fullscreen mode Exit fullscreen mode

That's Cloudflare. Not OpenAI. Not your code.

The Manual Fix

You need a backend endpoint. Period. Here's the minimal Express proxy:

// server.js
import express from 'express';
import fetch from 'node-fetch';

const app = express();

app.post('/api/chat', express.json(), async (req, res) => {
  try {
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
      },
      body: JSON.stringify(req.body)
    });

    const data = await response.json();
    res.json(data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(3001);
Enter fullscreen mode Exit fullscreen mode

This works. But now you're managing a proxy server. You need to handle streaming. You need to handle errors. You need to handle rate limiting.

Where It Gets Annoying

The proxy works. But now you can't see why your agent is failing. The proxy logs say 200 OK. The response is garbage. What happened inside?

You don't know if:

  • The prompt was too long and got truncated
  • The model hallucinated a tool call
  • Token limits were hit
  • The temperature setting caused randomness

This is where TracePilot comes in. One line change.

// Before — you're blind
const response = await openai.chat.completions.create({ model, messages });

// After — you see everything
const { result, spanId } = await tp.wrapOpenAI(
  () => openai.chat.completions.create({ model, messages }),
  messages
);
Enter fullscreen mode Exit fullscreen mode

That's it. Now every call is traced. You open the dashboard, see the exact prompt that was sent, the exact output, the tokens used, the latency. If it fails, you fork the trace, edit the prompt, and replay.

No more guessing. No more "works on my machine."

The Real Fix

Don't call OpenAI from the browser. Use a backend route. Instrument it with TracePilot. Now you have:

  • No CORS issues — backend-to-backend calls don't need CORS
  • No exposed keys — your API key stays server-side
  • Full visibility — every LLM call is traced, forkable, replayable

The proxy solves the network problem. TracePilot solves the debugging problem. Together, they solve the actual problem: your agent fails and you have no idea why.


That CORS error? It's a symptom. The real bug is you can't see what your agent is doing. Fix the visibility, and the network problems become trivial.


Debugging AI agents shouldn't feel like reading The Matrix.
Join other engineers who are building reliable autonomous workflows in our community: TracePilot Discord