Environment Information
- Broken: jruby 10.0.6.0 (3.4.5) and jruby 10.1.1.0 (4.0.0), arm64-darwin, OpenJDK 25
- Correct: jruby 9.4.15.0 (3.1 language level, predates
it), CRuby 3.4.8 and 4.0.6
Expected Behavior
Assigning to a local variable named it from inside a block does not write the outer variable as it does on CRuby. Instead, JRuby seems to create a fresh block-local it, so writes are silently lost and op-assignments (it += 1) raise NoMethodError: undefined method '+' for nil.
Reads of an outer it work correctly (fixed by #8767 in 10.0.1.0); only assignment seems broken. It seems broken even when the block declares an explicit parameter, where it cannot be the implicit parameter at all.
Actual Behavior
it = 0; 3.times { it = 5 }; p it # CRuby: 5 JRuby: 0 (silent wrong value)
it = 0; 3.times { it += 1 }; p it # CRuby: 3 JRuby: NoMethodError: undefined method '+' for nil
it = 0; [1,2].each { |e| it += e }; p it # CRuby: 3 JRuby: NoMethodError (explicit block param!)
it = 0; 3.times { p it } # CRuby: 0 0 0 JRuby: 0 0 0 (reads OK)
Or second example locally:
$ for v in jruby-9.4 jruby-10.0 jruby-10.1 3.1 3.4 4.0; do
for> echo "=== ruby@$v"
for> mise exec "ruby@$v" -- ruby -v -e 'it = 0; 3.times { it += 1 }; p it' || echo "FAILED (exit $?)"
for> done
=== ruby@jruby-9.4
jruby 9.4.15.0 (3.1.7) 2026-06-08 6114208a31 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +jit [arm64-darwin]
3
=== ruby@jruby-10.0
jruby 10.0.6.0 (3.4.5) 2026-06-11 716ad51f54 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +indy +jit [arm64-darwin]
NoMethodError: undefined method '+' for nil
<main> at -e:1
times at org/jruby/RubyFixnum.java:338
<main> at -e:1
FAILED (exit 1)
=== ruby@jruby-10.1
jruby 10.1.1.0 (4.0.0) 2026-07-22 aaa95d57e1 OpenJDK 64-Bit Server VM 21.0.12.1+1-LTS on 21.0.12.1+1-LTS +indy +jit [arm64-darwin]
NoMethodError: undefined method '+' for nil
<main> at -e:1
times at org/jruby/RubyFixnum.java:610
<main> at -e:1
FAILED (exit 1)
=== ruby@3.1
ruby 3.1.7p261 (2025-03-26 revision 0a3704f218) [arm64-darwin25]
3
=== ruby@3.4
ruby 3.4.8 (2025-12-17 revision 995b59f666) +PRISM [arm64-darwin25]
3
=== ruby@4.0
ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +PRISM [arm64-darwin23]
3
Real-world impact
Claude-assisted analysis/scoping.
sorbet-runtime 0.6.x uses it as a loop counter in lib/types/private/methods/signature.rb (it = 0 ... rest_count.times { ...; it += 1 }), so runtime validation of any sig'd splat-arg method crashes.
https://github.com/sorbet/sorbet/blob/e8f0eb82de923877387fa02fd6bbb2aac4801a66/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L228-L236
Via js-routes (which depends on sorbet-runtime), this can break rake assets:precompile for Rails apps on JRuby 10.
Workaround for sorbet-runtime users: SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL=never (not the same behaviour I guess, I am not personally particularly familiar with Sorbet).
Related issues
Environment Information
it), CRuby 3.4.8 and 4.0.6Expected Behavior
Assigning to a local variable named
itfrom inside a block does not write the outer variable as it does on CRuby. Instead, JRuby seems to create a fresh block-localit, so writes are silently lost and op-assignments (it += 1) raiseNoMethodError: undefined method '+' for nil.Reads of an outer
itwork correctly (fixed by #8767 in 10.0.1.0); only assignment seems broken. It seems broken even when the block declares an explicit parameter, whereitcannot be the implicit parameter at all.Actual Behavior
Or second example locally:
Real-world impact
Claude-assisted analysis/scoping.
sorbet-runtime 0.6.x uses
itas a loop counter inlib/types/private/methods/signature.rb(it = 0...rest_count.times { ...; it += 1 }), so runtime validation of any sig'd splat-arg method crashes.https://github.com/sorbet/sorbet/blob/e8f0eb82de923877387fa02fd6bbb2aac4801a66/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L228-L236
Via js-routes (which depends on sorbet-runtime), this can break
rake assets:precompilefor Rails apps on JRuby 10.Workaround for sorbet-runtime users:
SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL=never(not the same behaviour I guess, I am not personally particularly familiar with Sorbet).Related issues
it; notes "using the variable directlybehaves as in CRuby", which the cases above seem to contradict when an outer
itexists - may be the same issue/question thoughitspecial block variable hides local variable sometimes #8767 - similar shadowing issue for reads, fixed in 10.0.1.0; the write path seems still brokenitunexpected behavior #8952, JRuby10: an existingitvariable inside a block caused SyntaxError: ordinary parameter is defined #8723 — earlier outer/nesteditbugs, both fixed