Debugging a flow that runs but returns nothing useful
Written against Langflow 1.11.4. For how a component or feature works, use
the langflow-1-11-docs skill. This skill is about a narrower and more annoying
class of problem: the flow is valid, it runs, nothing errors, and the answer is
empty or wrong.
0. First, decide which layer is broken
Work outside in. Each step rules out a layer.
- Does the Playground produce the right answer? If yes, the flow is fine and your problem is in how you are calling it — go to §1.
- Did every node you expected actually run? Open the run and look at each node's output, not just the final one. A node with no output did not run; that is §2.
- Did a node run and return an empty value? That is §3 or §4.
- Did an Agent run but not call your tool? That is §5.
The single most useful habit: read the intermediate node outputs, not just the Chat Output. Langflow shows them, and an empty final answer almost always has an empty intermediate two hops upstream.
1. Calling a flow over the API
Two different auth schemes, and mixing them up is the most common 403.
BASE=http://localhost:7860
# Most routes take a JWT. With LANGFLOW_AUTO_LOGIN=true you can get one with no
# credentials at all:
TOKEN=$(curl -s --compressed $BASE/api/v1/auto_login \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
curl -s --compressed $BASE/api/v1/flows/?get_all=true -H "Authorization: Bearer $TOKEN"
# /api/v1/run REJECTS that JWT. It needs an API key instead.
curl -s --compressed -X POST \
-H "x-api-key: $LANGFLOW_API_KEY" -H "Content-Type: application/json" \
"$BASE/api/v1/run/<flow-id>?stream=false" \
-d '{"input_type":"chat","output_type":"chat","input_value":"your question"}'
/api/v1/run/...with a Bearer token returns "Since v1.5, LANGFLOW_AUTO_LOGIN requires a valid API key." That message is telling you to switch header, not to change a setting.- Responses are gzipped. Without
--compressed, curl hands you binary and your JSON parse dies on byte0x8b. If you see a UnicodeDecodeError orExpecting value: line 1 column 1, this is why. - Create an API key from the UI (your profile → API keys), or
POST /api/v1/api_key/with a JWT.
POST /api/v1/run builds only the subgraph that feeds the output you asked
for. So a node that is not upstream of the requested output never executes,
however correct it looks on the canvas. If a step seems to be skipped over the
API but works in the Playground, check whether it is actually connected to the
output you requested — a leaf node hanging off to one side will not run.
2. A node that did not run
- It is not upstream of the requested output (see above).
- It is downstream of a conditional branch that was not taken. This is normal and not a bug; see §3.
- It is downstream of a node that returned an empty value, and the component short-circuits on empty input.
3. If-Else: two traps, both silent
regex mode uses re.match, not re.search. The pattern is anchored at
position 0, so a pattern that would obviously match somewhere in the text fails
against every input, including a perfect one. Prefix with (?s).*:
# fails on everything:
##\s*Assumptions
# works:
(?s).*##\s*Assumptions
(?s) also makes . match newlines, which you almost always want when testing
a multi-line model output.
Do not converge an If-Else's True and False outputs onto one Chat Output. The branch that was not taken returns an empty message, and a shared Chat Output renders empty regardless of which way the condition went. Give each verdict its own Chat Output; the one on the excluded branch simply does not run. This is the most common cause of "my router works but the output is blank".
4. A Prompt Template that grew input fields you did not add
In its default mode the Prompt Template reads every { as the start of a
variable. So a prompt that shows the model a literal JSON example:
Return exactly: {"weight_a": 0.0800, "weight_b": 0.9200}
sprouts input fields named "weight_a" and friends, and the text the model
receives is mangled. Turn on double brackets (mustache mode) on the
component: single braces then become literal and variables move to
{{question}}.
If a prompt is silently losing part of its text, or the component has fields you cannot explain, this is almost always the cause.
5. An Agent that will not call a connected tool
The tool the model sees is not always the tool you think you built. Ask the Agent directly, in the Playground:
list the tools you have available, with their exact names and descriptions
What it reports is ground truth. Two things usually turn up:
- Names are not display names. The tool name comes from the component's output method, so it may look nothing like the label on the canvas.
- Several actions share one description. If a component's outputs have no individual descriptions, they all inherit the component's single one, and the model has N indistinguishable tools to choose between. It will pick badly and it will look like it is ignoring you.
Both are fixed on the component side — see the langflow-component-build skill.
Also: an Agent with nothing wired to its Tools port is not toolless. Some built-in tools are enabled by default on the component, so a "bare" Agent may still calculate or look up the date. If you are running an experiment that depends on the Agent having no tools, check the component's own toggles first, or your control arm is not a control.
6. When the model output is right but the flow's is wrong
Check for a type mismatch at a port. Langflow will connect ports whose types are compatible in principle, and a component that expects structured rows will happily accept a single text blob and produce one degenerate row. Read the intermediate output's shape, not just whether it is non-empty: a table with one row where you expected forty is the signature.
7. Known non-issue
One error-level OpenAI 400 per model per container process about
reasoning_effort not being supported for function tools. By design — Langflow
probes model capabilities by trying and reading the error. Not your flow. Only
investigate if it repeats many times in one container run, or if a streaming
run fails outright, since the retry only helps before the first chunk is sent.