How to Fix 'prompt template error in production' in LangChain (TypeScript)
When LangChain throws a prompt template error in production, it usually means the prompt variables you declared do not match the values you actually passed at runtime. In TypeScript, this shows up most often when a chain works in local tests but fails once real request payloads, optional fields, or agent inputs hit production.
The failure is usually one of these:
- •missing variable in the prompt template
- •wrong variable name
- •invalid message placeholder usage
- •a mismatch between
PromptTemplate,ChatPromptTemplate, and chain inputs
The Most Common Cause
The #1 cause is a mismatch between template variables and the object you pass into .invoke() or .call().
A typical LangChain error looks like this:
Error: Missing value for input variable `question`
or:
Error: PromptTemplate requires variables: ["topic"]. Received: ["query"]
Here’s the broken pattern versus the fixed pattern.
| Broken | Fixed |
|---|---|
Template expects question, code passes query | Template and input use the same key |
| Hidden runtime mismatch | Explicit typed input object |
import { PromptTemplate } from "@langchain/core/prompts";
// BROKEN
const prompt = PromptTemplate.fromTemplate(
"Answer this question: {question}"
);
const result = await prompt.format({
query: "What is LangChain?"
});
import { PromptTemplate } from "@langchain/core/prompts";
// FIXED
const prompt = PromptTemplate.fromTemplate(
"Answer this question: {question}"
);
const result = await prompt.format({
question: "What is LangChain?"
});
If you are using a chain, the same problem appears there too:
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a helpful assistant."],
["human", "{question}"],
]);
// BROKEN: passes `query`, but template expects `question`
const chain = prompt.pipe(model).pipe(new StringOutputParser());
await chain.invoke({ query: "Explain RAG" });
// FIXED
await chain.invoke({ question: "Explain RAG" });
In production, this often happens because:
- •request DTOs use one name, prompt templates use another
- •optional fields are omitted by upstream validation
- •refactors rename one side and not the other
Other Possible Causes
1. Using a message placeholder without passing an array
If you use MessagesPlaceholder, LangChain expects an array of messages, not a string.
import { ChatPromptTemplate, MessagesPlaceholder } from "@langchain/core/prompts";
// BROKEN
const prompt = ChatPromptTemplate.fromMessages([
["system", "You are a banking assistant."],
new MessagesPlaceholder("history"),
["human", "{input}"],
]);
await prompt.invoke({
history: "previous conversation",
input: "Check my balance",
});
// FIXED
await prompt.invoke({
history: [
{ role: "user", content: "Hi" },
{ role: "assistant", content: "Hello" },
],
input: "Check my balance",
});
2. Mixing up {} placeholders with literal braces
If your text contains JSON examples or code snippets, LangChain may try to treat them as variables.
// BROKEN
const prompt = PromptTemplate.fromTemplate(
'Return JSON like {"status": "ok"}'
);
// FIXED
const prompt = PromptTemplate.fromTemplate(
'Return JSON like {{\"status\": \"ok\"}}'
);
For templates with lots of JSON, use escaped braces or move structured examples into separate strings.
3. Passing undefined values from request payloads
TypeScript can still let undefined through if your runtime data is untrusted.
// BROKEN
await chain.invoke({
question: req.body.question, // can be undefined in production
});
// FIXED
if (typeof req.body.question !== "string" || !req.body.question.trim()) {
throw new Error("Invalid request: question is required");
}
await chain.invoke({
question: req.body.question,
});
4. Template variables declared in one place, chain input keys in another
This shows up when building reusable chains across services.
// BROKEN
const prompt = PromptTemplate.fromTemplate("Summarize {text}");
const input = { content: "Long document..." };
await prompt.format(input);
// FIXED
const input = { text: "Long document..." };
await prompt.format(input);
If you want to make this harder to break, define a TypeScript type for the input shape and reuse it everywhere.
How to Debug It
- •
Print the exact template variables
- •Check what LangChain thinks the template needs.
- •For
PromptTemplate, inspectprompt.inputVariables. - •For chat prompts, verify every
{variable}and everyMessagesPlaceholder.
- •
Log the runtime payload before invoking
- •Compare keys in your object with keys in the template.
- •Look for
undefined, empty strings, or renamed fields.
- •
Reduce to a minimal repro
- •Remove retrievers, tools, memory, and middleware.
- •Call only the prompt with hardcoded values.
- •If it works there, the bug is in your upstream data path.
- •
Turn on strict typing around your chain inputs
- •Define an interface for expected inputs.
- •Avoid passing raw
anyor loosely typed request bodies.
Example:
type SupportTicketInput = {
customerName: string;
issueSummary: string;
};
const prompt = PromptTemplate.fromTemplate(
"Customer {customerName} reported: {issueSummary}"
);
async function run(input: SupportTicketInput) {
return prompt.format(input);
}
Prevention
- •Keep template variables and TypeScript input types in one shared contract.
- •Validate external payloads before they reach LangChain.
- •Add a unit test that calls every prompt with realistic production-shaped data.
- •Treat JSON examples and message history as special cases; they fail differently than plain string prompts.
If you’re seeing this error only in production, assume it’s not “a LangChain problem” first. It’s usually an input contract problem that local mocks failed to catch.
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