Wire3 decoder pipeline causes 'too much recursion' on Firefox
Nobody has claimed this yet.
- Dominant language
- Haskell
- Stars
- 95
- Forks
- 21
- PR merge metrics
- No merged PRs in 30d
Description
Bug
Lamdera apps with large FrontendModel records crash on Firefox with:
Uncaught internal error: too much recursion
The app works fine on Chrome and works locally (lamdera live) but fails in production (lamdera deploy).
Root Cause
In extra/Lamdera/Wire3/Decoder.hs (line 459), decodeRecord generates a decoder pipeline using foldlPairs (|>):
succeedDecode (\a b c ... -> { field1 = a, field2 = b, ... })
|> andMapDecode decoder1
|> andMapDecode decoder2
...
|> andMapDecode decoderN
Each |> compiles to a nested function call in JS. For apps with deeply nested records (e.g. a FrontendModel containing OwnerData containing forms, lists, etc.), the total chain can reach 380+ levels of nesting.
In the compiled production JS, this becomes:
i(X, i(Y, i(Z, ... i(W, u(fn)) ...)))
// 380+ nested calls
Firefox's call stack limit is ~5000 frames. With ~12-15 frames per decoder step, 380 steps × ~13 frames ≈ ~5000 frames → stack overflow on Firefox.
Chrome has a higher stack limit (~8800+) so it works there.
How to Reproduce
- Create a Lamdera app with a
FrontendModelthat has many fields (including nested records with many fields) - Deploy to production (
lamdera deploy) - Open the deployed app in Firefox → "too much recursion" error
- Same app works in Chrome and locally with
lamdera live(non-optimized build)
Evidence
Analysis of the compiled production JS (frontend.*.js):
- Global max parenthesis nesting depth: 218 at the decoder chain
- 380 consecutive
i()calls (the compiledandMapDecodepipeline) - The
ifunction isfunction(r,t){ return A4(function(n){ return L$(n,r) }, t) }— each call adds to the stack
Suggested Fix
In Decoder.hs, chunk the foldlPairs into groups to limit nesting depth:
Instead of:
foldlPairs (|>) -- creates a single chain of N steps
Split into batches of ~50 fields, decode each batch separately, then compose the results. This would cap the nesting depth regardless of record size.
Alternatively, the encoder (Encoder.hs line 237-254) already avoids this issue by using a flat list:
encodeSequenceWithoutLength $ list fieldEncoders
A similar flat approach for the decoder would eliminate the problem entirely.
Environment
- Firefox (any version — all have stricter stack limits than Chrome)
- Lamdera production builds (optimized + minified JS)
- App with ~100+ total fields across nested FrontendModel records
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in extra/Lamdera/Wire3/Decoder.hs at decodeRecord around line 459 and inspect how foldlPairs (|>) builds the decoder pipeline. Compare the approach with Encoder.hs lines 237-254 and reproduce the issue using the Firefox, production-build scenario described. Done means large nested FrontendModel records no longer trigger Firefox's recursion error while existing decoder behavior remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- elm, haskell
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100