From 770d9ca03145bdc4d208f3e4cc29c795d87d8781 Mon Sep 17 00:00:00 2001 From: Jeremiah Church Date: Wed, 1 Apr 2026 16:53:22 -0400 Subject: [PATCH 1/2] fix: graciouslyFetch returns undefined on network error, crashes updates_for callers When a fetch fails (network error, CORS, timeout), graciouslyFetch() catches the error but implicitly returns undefined. Callers in updates_for_element.js then crash with TypeError when calling .text() on the undefined result. Fix: return new Response('') in the catch block so callers always receive a valid Response object. Also add defensive guards at both call sites for defense in depth. Fixes stimulusreflex/cable_ready#310 Co-Authored-By: Claude Opus 4.6 (1M context) --- javascript/elements/updates_for_element.js | 4 ++-- javascript/utils.js | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/elements/updates_for_element.js b/javascript/elements/updates_for_element.js index 768b45e9..dd5127a4 100644 --- a/javascript/elements/updates_for_element.js +++ b/javascript/elements/updates_for_element.js @@ -120,7 +120,7 @@ export default class UpdatesForElement extends SubscribingElement { const response = await graciouslyFetch(url, { 'X-Cable-Ready': 'update' }) - this.html[url] = await response.text() + if (response) this.html[url] = await response.text() } }) ) @@ -254,7 +254,7 @@ class Block { ) const frameTemplate = document.createElement('template') - frameTemplate.innerHTML = await frameResponse.text() + if (frameResponse) frameTemplate.innerHTML = await frameResponse.text() // recurse here to get all nested eager loaded frames await this.resolveTurboFrames(frameTemplate.content) diff --git a/javascript/utils.js b/javascript/utils.js index f636c282..39c0f6fb 100644 --- a/javascript/utils.js +++ b/javascript/utils.js @@ -202,6 +202,7 @@ async function graciouslyFetch (url, additionalHeaders) { return response } catch (e) { console.error(`Could not fetch ${url}`) + return new Response('') } } From c8d20ea66755a2926464c87ad443e1f27a994e93 Mon Sep 17 00:00:00 2001 From: Jeremiah Church Date: Wed, 1 Apr 2026 17:26:40 -0400 Subject: [PATCH 2/2] style: apply prettier-standard formatting to changed files Co-Authored-By: Claude Opus 4.6 (1M context) --- javascript/elements/updates_for_element.js | 3 ++- javascript/utils.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/javascript/elements/updates_for_element.js b/javascript/elements/updates_for_element.js index dd5127a4..acc009d0 100644 --- a/javascript/elements/updates_for_element.js +++ b/javascript/elements/updates_for_element.js @@ -254,7 +254,8 @@ class Block { ) const frameTemplate = document.createElement('template') - if (frameResponse) frameTemplate.innerHTML = await frameResponse.text() + if (frameResponse) + frameTemplate.innerHTML = await frameResponse.text() // recurse here to get all nested eager loaded frames await this.resolveTurboFrames(frameTemplate.content) diff --git a/javascript/utils.js b/javascript/utils.js index 39c0f6fb..39286c30 100644 --- a/javascript/utils.js +++ b/javascript/utils.js @@ -173,9 +173,15 @@ function safeObject (obj) { function safeStringOrArray (elem) { if (elem !== undefined && !Array.isArray(elem) && typeof elem !== 'string') - console.warn(`Operation expects an Array or a String, but got ${elem} (${typeof elem})`) + console.warn( + `Operation expects an Array or a String, but got ${elem} (${typeof elem})` + ) - return elem == null ? '' : Array.isArray(elem) ? Array.from(elem) : String(elem) + return elem == null + ? '' + : Array.isArray(elem) + ? Array.from(elem) + : String(elem) } function fragmentToString (fragment) {