Skip to content

Fix double render in Slack::PuzzlesController when notification fails#68

Closed
JuanVqz wants to merge 3 commits intomainfrom
fix-slack-double-render
Closed

Fix double render in Slack::PuzzlesController when notification fails#68
JuanVqz wants to merge 3 commits intomainfrom
fix-slack-double-render

Conversation

@JuanVqz
Copy link
Member

@JuanVqz JuanVqz commented Mar 6, 2026

Problem

send_message in Slack::ApplicationController called head :unprocessable_entity when rescuing a Slack::Web::Api::Errors::SlackError. This had two issues:

  1. Wrong response to Slack — the puzzle was already saved, but Slack was told the request failed (422).
  2. Double render — execution returned to Slack::PuzzlesController#create, which then called render json:, raising AbstractController::DoubleRenderError. Same class of bug as Fix double render in ApplicationController auth callbacks #66.
# Before — rendering side effect inside a helper method
def send_message(message, channel_id:)
  SlackClient::Client.instance.chat_postMessage(...)
rescue Slack::Web::Api::Errors::SlackError
  head :unprocessable_entity  # renders, but execution returns to caller
end

# In create — double render!
send_message(...)
render json: { response_action: "update", view: view }, status: :ok

Fix

send_message now logs the error, captures it in Sentry for full context, and returns without rendering. This means:

  • No rendering side effects leak into callers
  • create always responds 200 to Slack when a puzzle is saved successfully
  • The workaround return if performed? is no longer needed
def send_message(message, channel_id:)
  SlackClient::Client.instance.chat_postMessage(...)
rescue Slack::Web::Api::Errors::SlackError => e
  Rails.logger.error "Failed to send Slack message: #{e.message}"
  Sentry.capture_exception(e)
end

Tests

  • renders ok when puzzle fails validation — action returns 200 when puzzle doesn't save (no Slack call made)
  • renders ok even when Slack notification fails after puzzle is saved — action returns 200 even when send_message fails, confirming notification failure doesn't affect the Slack response

JuanVqz added 2 commits March 5, 2026 18:21
When send_message rescued a Slack::Web::Api::Errors::SlackError and called
head :unprocessable_entity, execution returned to create which then called
render json:, raising AbstractController::DoubleRenderError. Added
return if performed? to halt after send_message when it has already rendered.
@JuanVqz JuanVqz force-pushed the fix-slack-double-render branch from 690e92d to 51e07ff Compare March 6, 2026 00:25
@JuanVqz JuanVqz self-assigned this Mar 6, 2026
…entry

Calling head :unprocessable_entity inside send_message was a rendering
side effect that leaked into callers and wrongly told Slack the request
failed even when the puzzle was saved. Now logs the error, captures it
in Sentry for full context, and lets create always render 200 on success.
JuanVqz added a commit that referenced this pull request Mar 6, 2026
Replaces #66, #67, #68, #70 with a single cohesive fix:

- Merge check_session_expiry + check_user_token into one authenticate!
  callback, eliminating the double-render risk by design
- SessionsController and Slack::ApplicationController now each skip a
  single authenticate! instead of managing multiple callbacks
- Rename valid_slack_request? to verify_slack_request! and make it halt
  with head :unauthorized when signature verification fails
- Replace head :unprocessable_entity in send_message with logging and
  Sentry.capture_exception so notification failures never affect the
  HTTP response back to Slack
@JuanVqz
Copy link
Member Author

JuanVqz commented Mar 6, 2026

Superseded by #71 which addresses this and three other related bugs with a more robust design.

@JuanVqz JuanVqz closed this Mar 6, 2026
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.

1 participant