#11661·langchainjs

RunnableWithFallbacks.batch({ returnExceptions: true }) throws instead of returning errors

Author: yoarajotaCreated Sep 17, 2026Updated Sep 17, 2026

RunnableWithFallbacks.batch() throws Error: Not implemented. whenever returnExceptions: true is passed, even though the method's own type overloads declare that option as supported and return (RunOutput | Error)[].

Reproduction

@langchain/core 1.2.11, current main (778566e), Node v24.21.0.

typescript
import { RunnableLambda } from "@langchain/core/runnables";

const failing = RunnableLambda.from(() => {
  throw new Error("primary model down");
});
const fallback = RunnableLambda.from((x: string) => `fallback result for ${x}`);

const withFallbacks = failing.withFallbacks([fallback]);

await withFallbacks.invoke("input-1");
// -> "fallback result for input-1" (fine)

await withFallbacks.batch(["input-1", "input-2"]);
// -> ["fallback result for input-1", "fallback result for input-2"] (fine)

await withFallbacks.batch(["input-1", "input-2"], undefined, { returnExceptions: true });
// -> throws: Error: Not implemented.

Source: libs/langchain-core/src/runnables/base.ts, RunnableWithFallbacks.batch():

typescript
  async batch(
    inputs: RunInput[],
    options?: Partial<RunnableConfig> | Partial<RunnableConfig>[],
    batchOptions?: RunnableBatchOptions
  ): Promise<(RunOutput | Error)[]> {
    if (batchOptions?.returnExceptions) {
      throw new Error("Not implemented.");
    }

The three overload signatures immediately above this implementation (lines 3061-3074) advertise returnExceptions: true as a valid, typed input that resolves to (RunOutput | Error)[]. Nothing in the type signature indicates a runtime throw, so this fails silently until called: tsc accepts the call, and it blows up at runtime instead.

For comparison, calling failing.batch(["input-1", "input-2"], undefined, { returnExceptions: true }) directly on the plain RunnableLambda (no fallback wrapper) returns [Error, Error] instead of throwing. The base Runnable.batch() path handles the option; RunnableWithFallbacks.batch() is the one implementation that doesn't.

Why this matters for fallbacks specifically

withFallbacks() is the way to build multi-provider routing in this library: try model A, fall back to model B on failure. returnExceptions: true is what you reach for so one bad input in a batch doesn't abort the rest. That combination throws for every fallback runnable right now. The workaround is dropping down to a manual loop of individual invoke() calls with your own try/catch, giving up the batching this method exists to provide.

I hit this building model-routing and cost-optimization logic on @langchain/core fallbacks at Sagui.AI. We batch prompts through a primary/fallback pair and pass returnExceptions: true so one failed row doesn't take down the whole batch.

Prior art

I didn't find an existing issue or PR on this specific gap. #8533 was a different (mistaken) report that assumed the class didn't exist in TS at all; it was closed as not-planned once the reporter confirmed the class was already present, and didn't touch this returnExceptions behavior. This is on current main, not fixed by anything merged since.

Source: langchain-ai/langchainjs