Fix falsy computed object keys dropped on re-render (#901) - #977
Merged
RobLoach merged 1 commit intoAug 18, 2026
Merged
Conversation
A computed object key {(expr): value} that evaluates to a falsy value (0, '',
false) was treated as 'no key' because three sites in twig.expression.js tested
the truthiness of token.key instead of its presence. On the second render of a
compiled template the cached falsy key made the ':' operator drop the token from
the eval stack (params were already deleted), surfacing as 'Unexpected end of
object'; the object builder also misfiled a falsy-key pair as a value on the
first render.
Change the three truthiness checks to presence checks ('key' in token). Regular
operator tokens never carry a key property so they are unaffected; only falsy
computed keys are newly retained. The literal-key path already worked because
those keys are stringified up front.
Fixes twigjs#901
willrowe
approved these changes
Aug 18, 2026
willrowe
left a comment
Collaborator
There was a problem hiding this comment.
This looks correct to me. Good catch @youdie006!
Go ahead and merge if you don't see any issues @RobLoach.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #901.
Root cause
A computed object key --
{(expr): value}-- that evaluates to a falsy value (0,'',false) is treated as "no key" because three sites insrc/twig.expression.jstest the truthiness oftoken.keyinstead of its presence:The
:operator'sparse(if (token.key)). The first render evaluates the key expression, caches the result ontoken.key, and -- outside a loop -- deletestoken.params. On the second render of the same compiled template the cached key is0, soif (token.key)is false and theelse if (token.params)branch is also false (params were already deleted). The token falls through to the plain-operator branch and is dropped from the eval stack, which later surfaces asUnexpected end of object.The object builder in
object.end'sparse(... && token.key). On the first render a falsy-key pair isn't recognised as a key/value pair, so the key token is misfiled as a value and the entry silently disappears (m[0]comes back empty).The loop fixup (
loopTokenFixup.params && loopTokenFixup.key) has to use the same presence test, so a cached falsy key inside a loop is re-evaluated per iteration instead of being pinned to its first value.The literal-key path (
{0: 'x'}) already works because those keys are stringified up front (the existing "allow int 0 as a key" test, from #186). Only the computed-key path is affected: #284 added expression-as-key support and #337 hardened object-key stack handling, but neither covered a computed key that resolves to a falsy value.Reproduction
Fix
Change the three truthiness checks to presence checks (
'key' in token/'key' in loopTokenFixup). This is strictly more inclusive than the old test: regular operator tokens never carry akeyproperty, so they're unaffected; only falsy computed keys are newly retained.Tests
Two regression tests in
test/test.core.js(computed0key and computed empty-string key), each rendering the same compiled template twice and asserting the map is correct on the second render. Reverting the source with the tests kept turns both red; the fix turns them green. Full suite: 533 passing, eslint clean.Implemented with AI assistance; I reviewed and verified the diagnosis, fix, and tests.