How to Fix 'authentication failed in production' in LangGraph (TypeScript)
When LangGraph says authentication failed in production, it usually means your graph is calling a remote service with credentials that work locally but are missing, expired, or scoped incorrectly in the deployed environment.
In practice, this shows up when you move from .env-backed local dev to Vercel, Docker, ECS, Fly.io, or a CI runtime and one of the LangGraph client calls starts returning 401 Unauthorized or 403 Forbidden.
The Most Common Cause
The #1 cause is simple: your local code reads secrets from process.env, but production never got those env vars, or got the wrong ones.
With LangGraph TypeScript apps, this often happens when you initialize ChatOpenAI, AzureChatOpenAI, or a LangGraph client at module load time and assume .env will exist everywhere.
Broken vs fixed
| Broken pattern | Fixed pattern |
|---|---|
| Reads env vars implicitly at import time | Validates env vars before building the graph |
Works locally because .env is present | Fails in production because secret injection is missing |
| Hides the real failure until the first request | Fails fast with a clear config error |
// broken.ts
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph } from "@langchain/langgraph";
const model = new ChatOpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
export const graph = new StateGraph({
channels: {},
}).compile();
// later in prod:
// Error: AuthenticationError: 401 Incorrect API key provided
// fixed.ts
import { ChatOpenAI } from "@langchain/openai";
import { StateGraph } from "@langchain/langgraph";
function requireEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required env var: ${name}`);
}
return value;
}
const model = new ChatOpenAI({
apiKey: requireEnv("OPENAI_API_KEY"),
});
export const graph = new StateGraph({
channels: {},
}).compile();
The important part is not just “set the env var.” It’s making the app fail immediately if production config is incomplete. Otherwise you end up debugging a LangGraph execution error that is really just a deployment mistake.
Other Possible Causes
1) Wrong secret for the environment
A staging key accidentally shipped to production is common. The request authenticates, but against the wrong tenant or project.
const apiKey =
process.env.NODE_ENV === "production"
? process.env.OPENAI_PROD_KEY
: process.env.OPENAI_DEV_KEY;
If these values are swapped in your deployment platform, you get auth failures that look like service instability.
2) Missing LangSmith/LangGraph Platform credentials
If you’re using hosted tracing or remote graph execution, missing LANGSMITH_API_KEY, LANGCHAIN_API_KEY, or platform-specific tokens can produce auth errors even though your LLM key is valid.
# example deployment env
LANGSMITH_API_KEY=lsv2_...
LANGCHAIN_TRACING_V2=true
LANGCHAIN_PROJECT=my-prod-graph
A common symptom is a working model call but failing graph submission or trace upload.
3) Hardcoded localhost URLs in production
If your code points to a local LangGraph server or internal auth gateway, production will hit an endpoint that rejects it.
const baseUrl = process.env.LANGGRAPH_URL ?? "http://localhost:2024";
// production should be something like:
// https://your-langgraph-host.example.com
If the deployed app still targets localhost, auth can fail because it’s not even reaching the right service.
4) Token expired or rotated without redeploy
Some providers rotate keys. If your app stores secrets in a build artifact or old container image, production keeps using an expired credential.
# bad practice: baking secrets into image build args
docker build --build-arg OPENAI_API_KEY=sk-... .
Use runtime injection instead. Secrets should be available when the container starts, not compiled into the image.
How to Debug It
- •
Check which request is failing
- •Look at logs around the first
401 Unauthorizedor403 Forbidden. - •If it fails on model invocation, inspect provider keys.
- •If it fails on graph submission or tracing, inspect LangSmith/LangGraph credentials.
- •Look at logs around the first
- •
Log presence, not values
- •Confirm env vars exist in production without printing secrets.
- •Example:
console.log({ hasOpenAiKey: Boolean(process.env.OPENAI_API_KEY), hasLangSmithKey: Boolean(process.env.LANGSMITH_API_KEY), });
- •
Verify where config is loaded
- •Check whether
.envis only loaded locally viadotenv/config. - •In serverless and containers,
.envoften isn’t present unless explicitly mounted. - •Make sure config is read at runtime, not baked during build.
- •Check whether
- •
Reproduce with one minimal call
- •Strip the graph down to one model invocation.
- •If this fails:
then it’s not a graph issue.await model.invoke("test"); - •If this succeeds but compiled graph calls fail, check remote execution and tracing credentials.
Prevention
- •Validate all required secrets on startup with a strict config layer.
- •Keep separate keys for dev, staging, and prod, and name them clearly.
- •Add a deployment smoke test that runs one authenticated LangGraph/model call before traffic goes live.
- •Never rely on
.envbeing present outside local development.
If you want one rule to remember: treat authentication as infrastructure config first and application code second. In LangGraph TypeScript apps, most “authentication failed in production” errors are really missing env vars, wrong endpoints, or stale secrets masquerading as runtime bugs.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit