Skip to content

Guard _set_state! against a missing env info object - #229

Open
hikmetba-bit wants to merge 1 commit into
zombocom:mainfrom
hikmetba-bit:fix/set-state-nil-info-crash-225
Open

hikmetba-bit wants to merge 1 commit into
zombocom:mainfrom
hikmetba-bit:fix/set-state-nil-info-crash-225

Conversation

@hikmetba-bit

Copy link
Copy Markdown

Summary

Fixes #225.

_set_state! unconditionally did env[ENV_INFO_KEY].state = state. The heartbeat (which re-fires every second while a request is :active) and the timeout callback both run asynchronously via the scheduler thread and can execute after the request's RequestDetails info object is no longer present in env, raising:

NoMethodError: undefined method 'state=' for nil:NilClass

which terminates the scheduler thread outright — reported against real Puma/Rack production usage in the issue, along with a second crash with the same root cause in StateChangeLoggingObserver#log_state_change (undefined method 'state' for nil:NilClass) once the state-change notification fires with a nil-info env.

Fix

Return early from _set_state! when there's no info object to update — there's nothing meaningful to set state on or notify observers about at that point. Since StateChangeLoggingObserver#log_state_change re-reads env[ENV_INFO_KEY] itself rather than receiving the info object directly, skipping the observer notification here also prevents its crash, so one guard covers both reported traces.

This doesn't attempt to fix why the info object goes missing in the reporter's setup (that's environment/timing-specific and outside what's cleanly diagnosable from the issue alone) — it converts an unhandled crash that kills the scheduler thread into a safe no-op, which is what the reporter themselves suggested: "should rack-timeout check that env[ENV_INFO_KEY] is either a valid object... before trying to call state on it?"

Test

Added test/set_state_test.rb with 3 cases: _set_state! no longer raises when env has no info object (the regression case), it still updates state normally when the info object is present, and it still raises for an invalid state argument (existing validation preserved). Mutation-tested: reverting the source change makes the first test fail with the exact reported NoMethodError; with the fix, full suite is green (bundle exec rake test, 9/9).

🤖 Generated with Claude Code

_set_state! unconditionally called env[ENV_INFO_KEY].state = state.
The heartbeat (run every second while a request is active) and the
timeout callback both run asynchronously via the scheduler thread,
and can fire after the request's info object is no longer present in
env, raising:

  NoMethodError: undefined method 'state=' for nil:NilClass

which terminates the scheduler thread outright (reported against
real Puma/Rack usage, with a second, same-root-cause crash in
StateChangeLoggingObserver#log_state_change once the notification
callback fires with a nil-info env).

Return early when there's no info object to update instead of
crashing - there's nothing meaningful to set the state on or notify
observers about at that point, and StateChangeLoggingObserver reads
env[ENV_INFO_KEY] itself, so skipping the observer notification here
also prevents its crash.

Fixes zombocom#225.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

core.rb:203:in _set_state!': undefined method state=' for nil:NilClass (NoMethodError)

1 participant