Nested Loop Over Items not iterating for subsequent queries

Hi everyone​:waving_hand: ,

n8n: 1.64.0 (Docker) | Ubuntu 24.x (VPS Infomaniak, Europe/Paris)
Reverse proxy: Caddy (TLS) | DB: Postgres/pgvector | Ollama: 0.1.34
Chemin compose: /opt/n8n/docker-compose.yml

n8n-nodes-python-runtime
n8n-nodes-webpage-content-extractor
n8n-nodes-googleforms
n8n-nodes-google-search-console
n8n-nodes-google-pagespeed
@brave/n8n-nodes-brave-search

I’m having trouble with nested loops using the Loop Over Items node. My workflow runs Google searches for five different queries and processes each SERP result individually by checking against a Google Sheet, making an HTTP request, extracting the page content, and cleaning it. The Google Search node returns the expected 10 results per query (I can see all items right before the inner loop), but once the inner Loop Over Items node starts, it only iterates through the first batch of results from the first query. When the outer loop proceeds to the second query, the inner loop never runs; it seems to go directly to the “done” branch.

I have Batch size set to 1 on both Loop Over Items nodes, I connect the inner loop’s “loop” output to the processing nodes and send its “done” output back to the outer loop’s Continue input. The outer loop’s “loop” output goes to the Google Search node and its “done” output connects to a final node. Despite this, the inner loop still fails to iterate for subsequent queries.

Any ideas on what I’m missing? Thanks!

Hey @Zork welcome to the community?

Are you active to clarify something about your questions?

Thanks so much for the quick reply — yes, I’m here and happy to clarify!
I might be missing something very basic with nested loops. I tried a similar “loop inside a loop” pattern in another workflow as well and I keep seeing the same symptom: only the first batch/set is processed, the following ones jump straight to done. So I suspect I’m misunderstanding how the inner loop resets between outer iterations.

Environment (recap)
n8n 1.64.0 (Docker) on Ubuntu 24 (VPS Infomaniak), behind Caddy; Postgres/pgvector; Ollama 0.1.34. Community nodes include n8n-nodes-python-runtime, n8n-nodes-webpage-content-extractor, Brave Search, etc.

What I’m doing
Outer Loop Over Items (Batch size = 1) iterates 5 queries → Google Search (10 results per query).
Inner Loop Over Items (Batch size = 1) processes each result: Google Sheets lookup → If → HTTP Request → extractor → …
done(inner)Continue(outer), loop(outer) → Google Search. Expected: inner loop runs for all 10 results on each outer iteration.

What actually happens
Inner loop runs fine for the first outer iteration (query #1).
When the outer loop advances to query #2, the inner loop goes to done immediately (no iterations).

What I already tried

  • Replaced fixed references with current-item mapping ({{$json.link}} instead of $('node').item…).

  • Filtered invalid URLs before Sheets (^https?://).

  • Made Sheets lookup robust to avoid toString() on undefined:
    {{ String($json.link ?? $json.url ?? '') }}.

  • Double-checked wiring: Loop(inner) → ... → Continue(inner) and Done(inner) → Continue(outer).

  • Ran full executions (not only “Test step”).

Question / hypothesis
It feels like the inner loop state isn’t resetting between outer iterations.
Should I set Reset on the inner loop to an expression like:

{{ $node['Loop Over Items (inner)'].context['noItemsLeft'] }}

(or .context['done']) so each outer iteration starts a fresh inner loop?
I can test this right away and report back.

Thanks a lot

You’ve hit exactly the right probLem @Zork

the inner loop’s state isn’t resetting between outer iterations.

The solution now is you need to reset the inner loop’s context between outer iterations.

Add a function node between your outer loop and inner loop

// Reset inner loop context
const innerLoopNode = 'Loop Over Items (inner)';
await $node[innerLoopNode].clearContext();

return items;

Hi @Zelite**, thanks again!**
I think I finally understand the root cause (the inner loop keeps its state), but I’m still not getting it to work on my side — I’m surely holding it wrong :sweat_smile:

I reduced everything to a minimal repro (:backhand_index_pointing_up:):

  • Outer loop: URLs (Loop Over Items, batch size = 1) → 5 queries

  • Google Search → 10 results per query (so 50 items total)

  • Code node between Google Search and the inner loop (Run once for all items)

  • Inner loop: SERP (Loop Over Items, batch size = 1)

    • SERP(loop)HTTP RequestWait (just for visibility) → SERP(Continue)

    • SERP(done)URLs(Continue)

In the Code node I tried the snippet suggested above:

const innerLoopNode = 'SERP';
if ($node[innerLoopNode]?.clearContext) {
  await $node[innerLoopNode].clearContext();
}
return items;

What I see:

  • First outer iteration → inner loop runs through 10 items (OK).

  • Outer iteration #2..#5 → the inner loop jumps straight to done (0 items).

  • Counters: Google Search = 50 items total, SERP = 10 items total.

  • Full execution (not only Test step), both loops have batch size = 1.

So it looks like clearContext() isn’t actually resetting anything (maybe not a public API?).
I also tried clicking the Reset button on the inner loop node UI and… managed to create a lovely infinite loop :face_with_peeking_eye: (100% user error very likely).

Thanks a lot for your patience — promise I’ll stop bullying the poor Loop node as soon as I get this right :grinning_face_with_smiling_eyes:

TL;DR
If your inner Loop Over Items runs for the first outer iteration but then jumps straight to done for the next ones, the inner loop is keeping its state.
Fix: In the inner loop, set Options → Reset → Expression to reset only when new items arrive from the upstream node (e.g. Google Search):

{{ $prevNode.name === 'Google Search' }}

That resets the inner loop at the right time (when the next batch arrives), but not on its own Continue path — so no infinite loop.


Context / Environment

  • n8n 1.64.0 (Docker) on Ubuntu 24.x, reverse proxy Caddy, Postgres/pgvector, Ollama.

  • Repro workflow (simplified):

URLs (Loop Over Items, batch=1)   // outer loop: 5 queries
  → Google Search                 // 10 results per query
    → SERP (Loop Over Items, batch=1)   // inner loop
        SERP(loop) → HTTP Request → Wait → SERP(Continue)
        SERP(done) → URLs(Continue)

What happened:

  • Outer iteration #1 → inner processes 10 items :white_check_mark:

  • Outer iteration #2..#5 → inner went straight to done (0 items) :cross_mark:

  • Counters showed 50 items produced upstream, but inner had only processed 10.

This means the inner loop still had its internal “no items left” state from the previous outer iteration.


The fix (works reliably)

Use the built-in Reset option on the inner loop, but gate it with an expression so it only resets when the new items arrive from the upstream node, not when the loop feeds itself.

In SERP (the inner loop):

  • Options → Reset → Expression:
{{ $prevNode.name === 'Google Search' }}

If you have an extra node between Search and SERP (e.g. a small Code/Set), adapt it:

{{ ['Google Search', 'Clear context'].includes($prevNode.name) }}

Why it works:

  • When the 10 results arrive from Google Search, $prevNode.name is "Google Search"Reset = true → inner state is cleared and iteration starts fresh.

  • When the loop returns via HTTP Request → Wait → SERP(Continue), $prevNode.name is "Wait" (or "HTTP Request") → Reset = false → no reset on every inner turn → no infinite loop.

Important notes

  • Keep Batch size = 1 on both loops (outer + inner).

  • Do not set Reset to Fixed = true on the inner loop — that will reset on every iteration and cause an infinite loop.

  • A tiny Wait (100–300 ms) after HTTP calls can be helpful if you hit rate limits.


Credit / reference

I found the right direction thanks to this video (at the “Reset” part):
https://www.youtube.com/watch?v=VNcrPtcqpHo (thanks ChatGPT :slight_smile:

And thanks @Zelite for your time :wink:

Hopefully this saves someone else a few head-scratching hours. If a doc page exists (or should exist) for nested loops + Reset, I’d love to link it here. Cheers!

You are welcome mate kindly mark as the solution @Zork