LangServe deployment — route registration, streaming endpoint, playground errors (2026) — Fix Guide (2026) | AI Error Hub
Home Providers LangChain LangServe errors
LangChain Deployment · LangServe Severity: Medium HTTP n/a

LangServe — route registration, streaming, and playground failures

LangServe turns any LCEL chain into a FastAPI service with automatic streaming, batch, and playground endpoints. Deployment failures are usually about route setup, CORS, or streaming plumbing — not the chain itself.

By Ahmed R. · Senior AI Infrastructure Engineer Published 2026-07-26 Verified 2026-07-26

Quick fix (TL;DR)

Resolution: LangServe wraps a runnable in FastAPI routes: /invoke, /batch, /stream, /stream_log, plus /playground. Fix deployment issues by (a) calling add_routes(app, chain, path="/my-chain") with unique paths per chain, (b) enabling CORS if the playground is served on a different origin, (c) using async chains for streaming and configuring your ASGI server to not buffer, and (d) declaring ConfigurableFieldSpec when the chain needs session_id or user_id from clients.

Real error messages you'll see

These are the exact strings returned by the LangChain framework and its integrations when this error occurs. Copy-paste-searching any of them should land on this page.

add_routes error — non-unique path
ValueError: Path "/chain" is already used by another chain. Use distinct paths.
Playground blank — CORS blocks
Access to fetch at http://api.example.com/chain/playground_stream from origin http://localhost:5173 has been blocked by CORS policy
Streaming buffered by proxy
# curl --no-buffer http://api/chain/stream — chunks arrive fine
# Same chain behind nginx — response arrives as one blob
# proxy_buffering not disabled

Reference

LangServe endpoints auto-created by add_routes

EndpointPurpose
POST /invokeSync invocation
POST /batchBatched invocation
POST /streamServer-sent events with chunks
POST /stream_logDetailed streaming log with intermediate steps
POST /stream_eventsStreaming events (v2)
GET /playgroundInteractive UI for testing
GET /input_schemaJSON schema for chain input
GET /output_schemaJSON schema for chain output
GET /config_schemaJSON schema for configurable fields

Root causes, ranked by frequency

Based on developer reports across LangChain forums, GitHub issues, and Discord community during 2025–2026.

  • 24%
    Non-unique paths across add_routes calls. Two chains registered at "/chain" or nested paths collide.
  • 18%
    CORS not configured. Playground served on different origin from API; browser blocks.
  • 14%
    Streaming buffered by nginx / proxy. Missing proxy_buffering off and related settings.
  • 10%
    Sync chain in async LangServe context. Blocking the event loop kills throughput.
  • 8%
    Configurable fields not declared. Playground input has no field for session_id; user cannot test.
  • 7%
    Large per-request payloads exceed proxy limit. nginx 413 on RAG queries with large context.
  • 7%
    Missing dependencies for playground. langserve[all] extra not installed.
  • 12%
    Authentication not integrated. Endpoints publicly accessible; anyone can invoke.

Fixes — copy-paste solutions

Fix #1

Register chains with unique paths and clear input/output schemas

The core of LangServe — one chain, one path.

Use add_routes(app, chain, path="/chain-name"). Each chain gets its own subpath and its own set of endpoints. Declare input_type/output_type when the chain's inferred schema is not friendly.

langserve_app.py
from fastapi import FastAPI
from langserve import add_routes
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from pydantic import BaseModel

app = FastAPI(title="MyApp LLM Service")

# Chain 1: OpenAI-backed general QA
qa_prompt = ChatPromptTemplate.from_template("Answer briefly: {question}")
qa_chain = qa_prompt | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser()

# Chain 2: Claude-backed summarisation
summarise_prompt = ChatPromptTemplate.from_template(
    "Summarise this in 3 bullet points:\n\n{text}"
)
summarise_chain = (
    summarise_prompt | ChatAnthropic(model="claude-haiku-4-5-20251001") | StrOutputParser()
)

# Explicit input schemas for the playground UI
class QAInput(BaseModel):
    question: str

class SummariseInput(BaseModel):
    text: str

# Register each chain at a unique path
add_routes(app, qa_chain, path="/qa", input_type=QAInput, output_type=str)
add_routes(app, summarise_chain, path="/summarise", input_type=SummariseInput, output_type=str)

# For the app to run:
# pip install "langserve[all]" fastapi uvicorn
# uvicorn langserve_app:app --host 0.0.0.0 --port 8000

# Endpoints created:
#   POST /qa/invoke
#   POST /qa/batch
#   POST /qa/stream
#   GET  /qa/playground
#   POST /summarise/invoke
#   ...
Explicit input_type/output_type Pydantic models give the playground UI proper form fields. Without them, the playground shows a raw JSON textarea — usable but ugly.
Fix #2

Enable CORS and configure ASGI server for streaming

Two nginx / uvicorn settings you must get right for production.

For playground on a different origin, enable CORS. For streaming to work end to end, disable proxy buffering and configure uvicorn correctly.

cors_and_streaming.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from langserve import add_routes

app = FastAPI()

# CORS — allow the playground origin (and any other client)
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://app.example.com",       # your prod frontend
        "http://localhost:5173",         # local dev
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
    expose_headers=["*"],
)

add_routes(app, my_chain, path="/chain")

# Run with uvicorn — do NOT enable response buffering
# uvicorn langserve_app:app \
#     --host 0.0.0.0 --port 8000 \
#     --workers 4 \
#     --loop uvloop \
#     --http h11 \
#     --limit-concurrency 200 \
#     # NO --proxy-headers unless you actually have a proxy
nginx.conf
# nginx site block for LangServe

server {
    listen 443 ssl http2;
    server_name api.example.com;

    location /chain/ {
        proxy_pass http://langserve:8000;
        proxy_http_version 1.1;

        # === CRITICAL for streaming ===
        proxy_buffering off;              # do not buffer response
        proxy_cache off;                  # do not cache streamed
        proxy_read_timeout 300s;          # long timeout for slow streams
        proxy_connect_timeout 10s;
        proxy_send_timeout 300s;

        # Forward SSE / event-stream properly
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # === Increase body size for large RAG payloads ===
        client_max_body_size 32M;
        proxy_read_timeout 300s;
    }
}
Test streaming end-to-end from a real client, not just curl. Some SSE issues only appear with browser fetch or specific HTTP libraries.
Fix #3

Declare configurable fields for session_id and other per-request config

Playground and clients need to know what config fields exist.

When your chain uses RunnableWithMessageHistory or other configurable fields, declare them via configurable_fields so they appear in the playground and are documented in the schema.

configurable_fields.py
from langchain_core.runnables import ConfigurableFieldSpec
from langchain_core.runnables.history import RunnableWithMessageHistory
from langserve import add_routes

# ... your chat chain and history factory here ...

chain_with_history = RunnableWithMessageHistory(
    chain,
    get_session_history,
    input_messages_key="input",
    history_messages_key="history",
    history_factory_config=[
        ConfigurableFieldSpec(
            id="user_id", annotation=str, name="User ID",
            description="Unique user identifier", default="",
        ),
        ConfigurableFieldSpec(
            id="conversation_id", annotation=str, name="Conversation ID",
            description="Conversation ID within the user's history", default="",
        ),
    ],
)

# Register with LangServe — configurable fields become playground form inputs
add_routes(
    app,
    chain_with_history,
    path="/chat",
    per_req_config_modifier=lambda config, req: {
        **config,
        "configurable": {
            "user_id": req.headers.get("x-user-id", ""),
            "conversation_id": req.headers.get("x-conversation-id", ""),
        }
    },
)

# The per_req_config_modifier lets you pull config from HTTP headers/auth
# instead of trusting the client body. Critical for multi-tenant apps.
For any multi-tenant app, put user_id in the HTTP session / JWT — never trust it from the request body. The per_req_config_modifier is where you enforce this.

Prevention checklist

Ship these seven safeguards once and this error stops appearing in your logs.

  • One chain per path in add_routes; use distinct paths like /qa, /summarise.
  • Declare explicit Pydantic input_type/output_type for good playground UX.
  • Enable CORS with an explicit list of allowed origins; do not use allow_origins=["*"] with credentials.
  • For streaming: proxy_buffering off in nginx and any other proxy in the path.
  • Declare configurable fields with ConfigurableFieldSpec for session/user routing.
  • Use per_req_config_modifier to enforce security-sensitive config (user_id from auth, not body).
  • Deploy behind an authentication layer — never expose LangServe endpoints publicly without auth.

Frequently asked questions

LangServe auto-generates invoke/batch/stream/playground endpoints from any runnable. If you have a straightforward chain, that is a big time-saver. For complex logic (auth, custom validation, unusual response shapes), writing your own FastAPI routes and calling the chain manually may be cleaner.
Yes — it is a static React app served from the endpoint. In production, gate it behind your authentication middleware, or disable it with playground_type=None in add_routes.
Standard FastAPI middleware or dependencies. Attach an APIKeyHeader or JWT dependency to the LangServe router before add_routes. LangServe respects standard FastAPI auth patterns.
Yes for Cloud Run and similar container platforms. For Lambda, it works via Mangum but streaming is limited by Lambda's response size and timeout. For streaming workloads, prefer Cloud Run, ECS, or a long-running container.
For simple LCEL chain deployment, yes. For complex agent workflows, the LangChain team is nudging users toward LangGraph + LangGraph Cloud. Both are supported; pick based on your workload complexity.

Get the weekly AI-error digest

New fixes, provider status recaps, and one deep tutorial — every Tuesday. 8,400+ engineers.