Token are sent at a random order when rejoin a stream with VueJS

Hello, I tried to replicate the code in Join & rejoin streams - Docs by LangChain but I have an issue.
When I rejoin a stream, the token of the run id are sent at a random order.

I checked the EventStream content and here is the result:

Normal stream :

event: metadata
data: {"run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","attempt":1}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_1

event: values
data: {"messages":[{"content":"hello world","additional_kwargs":{},"response_metadata":{},"type":"human","name":null,"id":"d779ac94-76e5-45f7-8acc-fe53be53c106"}]}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_2

event: updates
data: {"SkillsMiddleware.before_agent":null}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_3

event: updates
data: {"PatchToolCallsMiddleware.before_agent":{"messages":"Overwrite(value=[HumanMessage(content='hello world', additional_kwargs={}, response_metadata={}, id='d779ac94-76e5-45f7-8acc-fe53be53c106')])"}}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_4

event: values
data: {"messages":[{"content":"hello world","additional_kwargs":{},"response_metadata":{},"type":"human","name":null,"id":"d779ac94-76e5-45f7-8acc-fe53be53c106"}]}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_5

event: messages
data: [{"content":"Hello","additional_kwargs":{},"response_metadata":{"model_provider":"openai"},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":null},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_6

event: messages
data: [{"content":"! How","additional_kwargs":{},"response_metadata":{"model_provider":"openai"},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":null},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_7

event: messages
data: [{"content":" can I","additional_kwargs":{},"response_metadata":{"model_provider":"openai"},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":null},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_8

event: messages
data: [{"content":" assist you today?","additional_kwargs":{},"response_metadata":{"model_provider":"openai"},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":null},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_9

event: messages
data: [{"content":"","additional_kwargs":{},"response_metadata":{"finish_reason":"stop","model_name":"mistral-medium-2508","model_provider":"openai"},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":null},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_10

event: messages
data: [{"content":"","additional_kwargs":{},"response_metadata":{},"type":"AIMessageChunk","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null,"tool_call_chunks":[],"chunk_position":"last"},{"mode":"generalist","agent_id":"1","thread_id":"767feaa2-1be1-46be-abe9-f36055e84cd9","run_id":"2fdc97d3-5399-4a7a-aaf6-a119a4346332","user_id":"anonymous","user_display_name":"anonymous","langgraph_step":3,"langgraph_node":"model","langgraph_triggers":["branch:to:model"],"langgraph_path":["__pregel_pull","model"],"langgraph_checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","checkpoint_ns":"model:32b91328-9835-b50e-a3f1-84dc6bf5f3e9","ls_provider":"openai","ls_model_name":"mistralai-medium","ls_model_type":"chat","ls_temperature":0.2}]
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_11

event: updates
data: {"model":{"messages":[{"content":"Hello! How can I assist you today?","additional_kwargs":{},"response_metadata":{"finish_reason":"stop","model_name":"mistral-medium-2508","model_provider":"openai"},"type":"ai","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null}]}}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_12

event: values
data: {"messages":[{"content":"hello world","additional_kwargs":{},"response_metadata":{},"type":"human","name":null,"id":"d779ac94-76e5-45f7-8acc-fe53be53c106"},{"content":"Hello! How can I assist you today?","additional_kwargs":{},"response_metadata":{"finish_reason":"stop","model_name":"mistral-medium-2508","model_provider":"openai"},"type":"ai","name":null,"id":"lc_run--019d0039-cca6-7031-85a1-0687a2edb2d0","tool_calls":[],"invalid_tool_calls":[],"usage_metadata":null}]}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_13

event: updates
data: {"TodoListMiddleware.after_model":null}
id: 2fdc97d3-5399-4a7a-aaf6-a119a4346332_event_14


Rejoin Stream :

Does anybody know the reason?

The text I get vs the true order:

k began to cool. It was not yet Earth—just a fiery embryo, bombarded by asteroids and comets, its surface a hellscape of lava and toxic gases. But deep within its core, something extraordinary was happening. Heavy metals sank, forming a molten heart, while lighter elements rose to create a crust. Over millions of years, the planet’s violent infancy gave way to a fragile stability. Then, from the void, came a gift: water. Comets, those icy wanderers of the cosmos, crashed into the young planet, depositing their precious cargo. Steam rose from the cooling rocks, condensing into clouds, and for rain fell theans formed and mysterious cr the first of life#### **Act: The Dawn** In, dark of the primordial stirred hydro, wherealding water met-rich, simple to danceo acids together, forming proteinsatsced., in a moment al cell was born—CA the Last Common Anc billions, life microscopic a silentphony of archa invented, filling with oxygen poison to life time, but a of to come Oxygen painted blue the planet for more. Then an creativity, lifeified. The Camb Explosion,54 million years ago the birth, claws, and jaws te with bizarre wonderful *oc a append; *Halligeniny that walked stil;Pia ancestor all vertebrates,. --- #### ** Age** The land once bar. crept, their roots breaking, leaves sunlightsects, thenians, then. of began. 16 million years ruledBiosaur towering like skrapers, strippedetops long neck. *rann*, the, crushed with its jaws. *etzatl rept with a wings small planeared over rivertas trem their footsteps ro with their. But sym not lastxty million years ago from space six miles into what now Mexico. The released equivalent bombs.fires ra Tsamis sc coast winter, the sun. The those tit, per,, life., no than ratsrow, seeds. They survived. their survival the future
just a fiery embryo, bombarded by asteroids and comets, its surface a hellscape of lava and toxic gases. But deep within its core, something extraordinary was happening. Heavy metals sank, forming a molten heart, while lighter elements rose to create a crust. Over millions of years, the planet’s violent infancy gave way to a fragile stability. Then, from the void, came a gift: water. Comets, those icy wanderers of the cosmos, crashed into the young planet, depositing their precious cargo. Steam rose from the cooling rocks, condensing into clouds, and for the first time, rain fell upon the Earth. Oceans formed, vast and mysterious, cradling the first whispers of life. --- #### **Act I: The Dawn of Life** In the deep, dark abyss of the primordial oceans, something stirred. Around hydrothermal vents, where scalding water met mineral-rich rocks, simple molecules began to dance. Amino acids linked together, forming proteins. Fats coalesced into membranes. And then, in a moment of cosmic alchemy, the first cell was born—**LUCA**, the Last Universal Common Ancestor. For billions of years, life remained microscopic, a silent symphony of bacteria and archaea. They invented photosynthesis, filling the air with oxygen—a poison to most life at the time, but a promise of things to come. The Great Oxygenation Event painted the skies blue and turned the planet into a haven for more complex beings. Then, in an explosion of creativity, life diversified. The Cambrian Explosion, 541 million years ago, saw the birth of eyes, claws, shells, and jaws. The oceans teemed with bizarre and wonderful creatures: *Anomalocaris*, a giant predator with grasping appendages; *Hallucigenia*, a spiny worm that walked on stilts; and *Pikaia*, a humble ancestor of all vertebrates, including us. --- #### **Act II: The Age of Titans** The land, once barren, was conquered. Plants crept ashore, their roots breaking rock, their leaves drinking sunlight. Insects followed, then amphibians, then reptiles. The age of dinosaurs began. For 165 million years, they ruled. *Brachiosaurus*, towering like skyscrapers, stripped treetops with its long neck. *Tyrannosaurus rex*, the apex predator, crushed bones with its massive jaws. *Quetzalcoatlus*, a flying reptile with a wingspan of a small plane, soared over river deltas. The Earth trembled with their footsteps, roared with their calls. But the symphony could not last forever. Sixty-six million years ago, a rock from space, six miles wid

You can find a simpler exemple, with the full text well ordered in the last messages.

What you’re seeing on reconnect is usually expected behavior with SSE streams in LangGraph / LangChain: token chunks are optimized for live delivery, not guaranteed replay, so when a client rejoins, events can arrive in a different order than they were originally emitted. That’s especially noticeable because the stream may send different event types (messages, updates, values) independently, and after reconnect they can interleave unexpectedly.

A few details that help explain it:

  • The streaming endpoint is designed for real-time delivery, so chunks emitted before reconnect are not guaranteed to be replayed token-by-token.

  • When rejoining (join_stream / join), you may receive partial messages together with updates or final values, and those are not always delivered in original emission order.

  • In distributed deployments (multiple workers, API instances, Redis propagation, etc.), reconnect timing can make ordering differences more visible.

Practical ways to handle it

1) If you need correctly ordered final text, use the authoritative final state

Instead of relying on streamed token order after reconnect, fetch the final merged result via client.runs.join() or thread state (/threads/<THREAD_ID>/state). That gives you the canonical message state in correct order.

2) If you still want streaming after reconnect, order chunks client-side using SSE event ids

Since SSE events include ids like <run_id>_event_<N>, you can extract the sequence number, buffer incoming chunks, and flush them in numeric order.

const buffer = new Map();
let lastFlushed = 0;

function flushContiguous() {
  while (buffer.has(lastFlushed + 1)) {
    lastFlushed += 1;
    const chunk = buffer.get(lastFlushed);
    appendChunk(chunk);
    buffer.delete(lastFlushed);
  }
}

const es = new EventSource(streamUrl);

es.addEventListener('messages', (ev) => {
  const id = ev.lastEventId || ev.id || '';
  const seq = parseInt(id.split('_event_').pop(), 10);
  const data = JSON.parse(ev.data);

  buffer.set(seq, data);
  flushContiguous();
});

That usually makes reconnect behavior much more deterministic.

3) Make sure only one active stream exists per run

A common source of duplication/interleaving is accidentally opening multiple EventSource connections for the same run. Always close the old stream before reconnecting.

4) For stronger reconnect reliability, rely on checkpoints / thread state

If you need resumable incremental UX, a good pattern is:

  1. fetch current thread state on reconnect,
  2. restore already-produced content,
  3. then continue streaming only new output.

Rule of thumb

  • Need final ordered output → use join() or thread state

  • Need low-latency live tokens → stream + client-side sequencing

That combination tends to be the most reliable in real deployments.

Relevant docs:

Thanks you for the answer, I will apply that!

1 Like