Skip to content

Fix BC-10091763201: send turbo-stream Accept from autosave to stop 406#2979

Open
jeremy wants to merge 1 commit into
mainfrom
bugs/BC-10091763201
Open

Fix BC-10091763201: send turbo-stream Accept from autosave to stop 406#2979
jeremy wants to merge 1 commit into
mainfrom
bugs/BC-10091763201

Conversation

@jeremy

@jeremy jeremy commented Jul 14, 2026

Copy link
Copy Markdown
Member

Summary

Fixes BC-10091763201 — POST to /cards/:id returns 406 Not Acceptable on drafted-card autosave.

Problem

Typing in a new/drafted card's title or description, then blurring / pressing Enter / waiting for the 3s debounce, fires an autosave that 406s. The comment editor is unaffected (it submits through Turbo's own form machinery).

Solution

app/javascript/helpers/form_helpers.js — pass responseKind: "turbo-stream" to the FetchRequest.

const request = new FetchRequest(form.method, form.action, {
  body: new FormData(form),
  responseKind: "turbo-stream"
})

Root Cause

submitForm() constructed the FetchRequest with no responseKind, so @rails/request.js defaulted to Accept: text/html. The autosave POST (with hidden _method=patch) routes to CardsController#update, whose respond_to registers only turbo_stream and json — no html — so Rails raises ActionController::UnknownFormat → 406. Every other @rails/request.js caller in the app already passes responseKind: "turbo-stream".

No data loss: @card.update! commits before respond_to raises. What was actually lost is the update.turbo_stream.erb side effects (draft-container morph, closure toggle) — which now apply correctly after each save — plus a 406/UnknownFormat on every drafted-card autosave in the logs.

Origin

The missing responseKind dates to 4121f68f7 "Auto-save edits to bubble title" (Kevin McConnell) — a latent gap. It was masked while CardsController#update ended in redirect_to @card (a 302 an html-Accept fetch happily followed); it started 406ing when b05ecb780 (Mike Dalessio, 2025-11-22) removed the redirect and moved to a turbo_stream template. So: JS-side latent gap, surfaced by a server-side change.

Scope

submitForm has exactly one caller — auto_save_controller.js, attached only to the drafted-card form — so no other flow is affected.

Testing

  • Verified sole caller can handle the turbo-stream response (request.js auto-renders it)
  • Manually verify draft autosave returns 200 and the draft container morphs without disturbing the focused field

Bug Card

https://app.basecamp.com/2914079/buckets/27/card_tables/cards/10091763201

Root cause: submitForm() built a FetchRequest with no responseKind, so
@rails/request.js defaulted to Accept: text/html. The drafted-card autosave
POSTs (with _method=patch) to CardsController#update, whose respond_to only
registers turbo_stream and json — no html — so Rails raises UnknownFormat and
returns 406 Not Acceptable on every autosave. (No data loss: @card.update!
commits before respond_to raises; what's lost is the turbo_stream UI update
and the log fills with 406s.)

Fix: pass responseKind: "turbo-stream" so the request declares the Accept
header the action serves. Sole caller is the draft autosave, so this also lets
the update.turbo_stream.erb side effects (draft container morph, closure toggle)
actually apply after each save, as intended.

Card: https://app.basecamp.com/2914079/buckets/27/card_tables/cards/10091763201
Copilot AI review requested due to automatic review settings July 14, 2026 21:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes drafted-card autosave requests returning 406 Not Acceptable by ensuring autosave form submissions advertise they accept Turbo Stream responses, matching CardsController#update’s respond_to formats.

Changes:

  • Update submitForm() to pass responseKind: "turbo-stream" when constructing FetchRequest, so the request uses the correct Accept header and receives the update.turbo_stream response.

Tip

If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7aa24d6639

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const request = new FetchRequest(form.method, form.action, {
body: new FormData(form)
body: new FormData(form),
responseKind: "turbo-stream"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Prevent stale autosave streams from clobbering edits

With this option, request.js automatically renders the CardsController#update turbo-stream response, whose first stream morph-replaces the whole card_container. In the draft autosave flow the 3s debounce can fire while the user keeps typing, and auto_save_controller has no in-flight/stale-response guard; if the response for an older snapshot returns after newer keystrokes, the morph renders that older server state back into the same draft form. Unlike the normal edit form, the draft form has no local-save restore hook, so those newer edits can be overwritten or re-saved as the old value on slow connections.

Useful? React with 👍 / 👎.

@jeremy

jeremy commented Jul 19, 2026

Copy link
Copy Markdown
Member Author

🤖 Independent verification while chasing card 10036823881 (Firefox paste/title-focus report): reproduced the 406 autosave storm locally (Playwright Firefox, real table paste into a draft card — 302 create → 200 → 406, 406, 406…), and this PR's fix verifies: same scenario returns 200s.

One caveat found during verification: with the fix applied, the now-rendered cards#update turbo-stream dropped keystrokes typed while autosave responses landed (typed abcdefghijklmnop into the title mid-autosave, got abcde_ghij_lmnop — lost exactly the characters typed as each response arrived). Worth checking that the update turbo-stream doesn't replace/touch the focused title field or its form — on Windows Firefox this may present as the full focus-theft the customer reported.

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.

2 participants