Spot the error in this transformer block that silently dropped its residuals
Click any words you think contain an error. Click again to unmark.
Both residual additions are missing. The block overwrites the stream instead of writing into it, killing the skip-path gradient flow that makes deep transformers trainable.
Imagine a relay race where each runner must hand the baton to the next runner. Now picture a relay where each runner throws the old baton away and starts running with a brand new one they brought from home. The baton never travels the full distance because it gets replaced at every handoff. The residual connection in a transformer is the baton. Each sublayer is supposed to take the running stream, do its work, and add its result back so the next sublayer gets both the original baton and the new contribution. When the code overwrites the variable instead of adding to it, every layer throws the old baton away. The relay falls apart and the deep network cannot learn.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
This is a small bug with large consequences. The buggy block has the right structure (norm, attention, norm, FFN) in the right order, but it is missing the two additive skip paths that distinguish a transformer block from a 1990s-style deep network. The result looks like a transformer in shape, fails to train like a transformer in behavior, and is one of the most common bugs people make when implementing a block from scratch.
This walkthrough explains the bug, the fix, and why exactly the two missing additions matter for both gradient flow and the residual-stream picture that interpretability and stability arguments build on. The fix is two characters per line, but the conceptual content of those two characters is much of what makes modern deep learning work.
The bug and the fix
The buggy block:
def block(x):
a = attn(norm1(x))
x = ffn(norm2(a))
return x
Follow x through the function. On entry, x is the residual stream input. Line 2 computes the attention output of a normalized version of x and binds it to a. The original x is no longer reachable from a. Line 3 computes the FFN output of a normalized version of a and binds the result to x, overwriting the function argument. The original residual stream has been thrown away twice.
The correct version:
def block(x):
a = x + attn(norm1(x))
x = a + ffn(norm2(a))
return x
The two new x + and a + are the residual additions. Each sublayer output is now ADDED to the stream it read from, not used to overwrite it.
The difference between the two versions is two + operators. The behavioral difference is the difference between a network that trains and one that does not.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
Real products, models, and research that use this idea.
- ResNet (He et al. 2015) introduced the identity skip path that the transformer block inherits; the same gradient-flow argument applies.
- Llama 3 reference implementations all encode the pre-norm residual exactly as h = h + attention(norm(h)) followed by h = h + ffn(norm(h)).
What an interviewer would ask next. Try answering before peeking at the approach.
QHow does the residual connection interact with weight initialization in deep transformers?
Sublayer outputs at initialization need to be small relative to the residual; standard init scales the last layer in each branch to make this true. Without the residual, this init scheme is wrong.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Reading the code as correct because it 'looks like a block' or fixing only one of the two missing residuals. The pre-norm recipe needs both: a = x + attn(norm1(x)), then x = a + ffn(norm2(a)).
60 second bullets to scan on the way to the call.
What the pre-norm block looks like with both residuals intact
Why the identity in 1 + f'(x) is what saves the gradient at depth
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.