From c641f3f9b932ccb84d027b9c54e418fec4b7be91 Mon Sep 17 00:00:00 2001 From: Piclaw Date: Wed, 22 Apr 2026 16:23:31 -0700 Subject: [PATCH] Fix infinite save retry loop and method check bugs - Add retry limit (3 attempts) to saveFileContents() to prevent infinite retry loop when file writes fail. Shows error message after retries exhausted instead of silently retrying forever. This fixes the 'reloading every 1-2 seconds' behavior reported in #460. - Clear save retry timeout on disconnect to prevent retries from continuing after switching boards or disconnecting. - Fix _isMethodAllowed() missing parentheses on toUpperCase call (was passing function reference instead of calling it). - Add missing await on _isMethodAllowed() call in _fetch() so the method permission check actually evaluates the result. - Fix missing closing quote in save failure message template literal. Fixes #460 --- js/common/web-file-transfer.js | 4 ++-- js/script.js | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/js/common/web-file-transfer.js b/js/common/web-file-transfer.js index cfedb9d4..d918e57a 100644 --- a/js/common/web-file-transfer.js +++ b/js/common/web-file-transfer.js @@ -89,7 +89,7 @@ class FileTransferClient { }; if (fetchOptions.method && fetchOptions.method.toUpperCase() != 'OPTIONS') { - if (!this._isMethodAllowed(fetchOptions.method)) { + if (!await this._isMethodAllowed(fetchOptions.method)) { if (fetchOptions.method.toUpperCase() == "MOVE") { // This should only happen if rename is used and the user doesn't have latest version console.warn("Please upgrade to the latest version of CircuitPython. Allowing MOVE for now."); @@ -114,7 +114,7 @@ class FileTransferClient { async _isMethodAllowed(method) { if (this._allowedMethods) { - return this._allowedMethods.includes(method.toUpperCase); + return this._allowedMethods.includes(method.toUpperCase()); } return false; diff --git a/js/script.js b/js/script.js index 6b32d719..7ae4af0e 100644 --- a/js/script.js +++ b/js/script.js @@ -462,6 +462,8 @@ async function loadEditor() { var editor; var currentTimeout = null; +var saveRetryCount = 0; +const MAX_SAVE_RETRIES = 3; // Save the File Contents and update the UI async function saveFileContents(path) { @@ -482,8 +484,9 @@ async function saveFileContents(path) { if (await workflow.writeFile(path, contents, offset)) { setFilename(workflow.currentFilename); setSaved(true); + saveRetryCount = 0; } else { - await showMessage(`Saving file '${workflow.currentFilename} failed.`); + await showMessage(`Saving file '${workflow.currentFilename}' failed.`); } } catch (e) { console.error("write failed", e, e.stack); @@ -491,7 +494,14 @@ async function saveFileContents(path) { if (currentTimeout != null) { clearTimeout(currentTimeout); } - currentTimeout = setTimeout(saveFileContents, 2000); + saveRetryCount++; + if (saveRetryCount < MAX_SAVE_RETRIES) { + console.log(`Save retry ${saveRetryCount} of ${MAX_SAVE_RETRIES}...`); + currentTimeout = setTimeout(saveFileContents, 2000); + } else { + saveRetryCount = 0; + await showMessage(`Saving file '${workflow.currentFilename}' failed after multiple attempts. Check your connection and try again.`); + } } } @@ -535,6 +545,11 @@ async function onTextChange(update) { } function disconnectCallback() { + if (currentTimeout != null) { + clearTimeout(currentTimeout); + currentTimeout = null; + } + saveRetryCount = 0; updateUIConnected(false); }