Skip to content

fix(core): return a matched RunnableBranch's falsy output instead of the default - #11617

Open
Khizar Chaudhry (Khizarc) wants to merge 1 commit into
langchain-ai:mainfrom
Khizarc:fix/runnable-branch-falsy-output
Open

Khizar Chaudhry (Khizarc) wants to merge 1 commit into
langchain-ai:mainfrom
Khizarc:fix/runnable-branch-falsy-output

Conversation

@Khizarc

Copy link
Copy Markdown

Fixes #11583

Problem

RunnableBranch._invoke decided whether a branch had run by testing its output:

if (conditionValue) {
  result = await branchRunnable.invoke(...);
  break;
}
if (!result) {                       // <-- 0, "", false, null all land here
  result = await this.default.invoke(...);
}

A branch that matched and returned a falsy value is indistinguishable from no branch
matching at all, so its output was discarded and the default ran instead. No error is
raised, so the call silently returns the wrong value:

const branch = RunnableBranch.from([
  [(x: number) => x > 0, (x: number) => 0],
  (x: number) => -1,
]);
await branch.invoke(5);   // expected 0, got -1

Fix

Track whether a branch matched instead of inferring it from the output.

The streaming path at _streamIterator already avoids this with stream === undefined,
which is safe there because a stream object is never falsy. I used an explicit flag
here rather than mirroring that check, because a branch may legitimately return
undefinedresult === undefined would still fall through in that case. There is a
test covering it.

Tests

Added to runnable_branch.test.ts:

  • a matched branch returning 0, "", false or null is returned, not the default
  • a matched branch returning undefined is returned, not the default
  • the default is still used when no condition matches
  • batch preserves falsy branch outputs

Verified these fail before the change and pass after:

before after
runnable_branch.test.ts 3 failed, 5 passed 8 passed

Full src/runnables/tests/ suite: 170 passed, with the same 2 pre-existing
streamEvents failures present on a clean checkout. No type errors.

…the default

RunnableBranch._invoke decided whether a branch had run by testing its output
with `if (!result)`. A branch that matched and returned 0, "", false, null or
undefined is indistinguishable from no branch matching under that test, so its
output was discarded and the default branch ran instead, silently.

Track whether a branch matched rather than inferring it from the output. The
streaming path already avoids this by testing `stream === undefined`, which is
safe there because a stream object is never falsy; an explicit flag is used here
because a branch may legitimately return undefined.

Fixes langchain-ai#11583

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Pfaw9PDrV9hnhQCMdYvTR
@changeset-bot

changeset-bot Bot commented Sep 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 1faa14b

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(core): RunnableBranch._invoke falls through to default on falsy branch output (0/""/false)

1 participant