From 0970ab3a6bc63f54a791db430d386513f85b203a Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Fri, 9 Jan 2026 22:26:49 -0500 Subject: [PATCH 01/11] Remove note about setting the wardriving channel in the README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 1f6a6f8..a700e70 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ A browser-based Progressive Web App for wardriving with MeshCore devices. Connec ### Before you start -- Make sure you have the **wardriving channel** set on your companion. - Take a **backup of your companion** (this webapp is beyond experimental). ### Android From 44b58f3d29c3388c369e961de2ebe508c658919a Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 11:50:41 -0500 Subject: [PATCH 02/11] Implement remote debug logging with rate limiting and batch submission --- content/wardrive.js | 212 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/content/wardrive.js b/content/wardrive.js index 512514d..168ffe0 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -12,6 +12,218 @@ import { WebBleConnection, Constants, Packet, BufferUtils } from "./mc/index.js" const urlParams = new URLSearchParams(window.location.search); const DEBUG_ENABLED = urlParams.get('debug') === 'true' || false; // Set to true to enable debug logging by default +// ---- Remote Debug Configuration ---- +// Enable remote debug logging via URL parameters (?debuguser=1&debugkey=) +// When enabled, all console output is batched and POSTed to meshmapper.net/livedebug.php +const REMOTE_DEBUG_USER = urlParams.get('debuguser') === '1'; +const REMOTE_DEBUG_KEY = urlParams.get('debugkey') || null; +let REMOTE_DEBUG_ENABLED = REMOTE_DEBUG_USER && REMOTE_DEBUG_KEY; // Can be disabled on no_session error + +// Remote Debug Queue State +const REMOTE_DEBUG_ENDPOINT = 'https://meshmapper.net/livedebug.php'; +const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch +const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds +const REMOTE_DEBUG_RATE_LIMIT = 10; // Max logs per second +const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval + +const debugLogQueue = { + messages: [], // Array of {date: , message: } + flushTimerId: null, // Timer ID for periodic flush + rateResetTimerId: null, // Timer ID for rate limit reset + logsThisSecond: 0, // Current rate counter + droppedCount: 0, // Logs dropped due to rate limiting + isProcessing: false // Lock to prevent concurrent flush +}; + +// Store original console methods before overriding +const originalConsoleLog = console.log.bind(console); +const originalConsoleWarn = console.warn.bind(console); +const originalConsoleError = console.error.bind(console); + +/** + * Queue a log message for remote debug submission + * Handles rate limiting (10/sec) and batch size limits + * @param {string} level - Log level (log, warn, error) + * @param {Array} args - Arguments passed to console method + */ +function queueRemoteDebugLog(level, args) { + if (!REMOTE_DEBUG_ENABLED) return; + + // Rate limiting check + if (debugLogQueue.logsThisSecond >= REMOTE_DEBUG_RATE_LIMIT) { + debugLogQueue.droppedCount++; + return; // Drop this log + } + debugLogQueue.logsThisSecond++; + + // Serialize arguments to string + const messageParts = args.map(arg => { + if (arg === null) return 'null'; + if (arg === undefined) return 'undefined'; + if (typeof arg === 'object') { + try { + return JSON.stringify(arg); + } catch { + return String(arg); + } + } + return String(arg); + }); + + // Prepend level prefix for warn/error + let prefix = ''; + if (level === 'warn') prefix = '[WARN] '; + if (level === 'error') prefix = '[ERROR] '; + + const logEntry = { + date: Date.now(), + message: prefix + messageParts.join(' ') + }; + + debugLogQueue.messages.push(logEntry); + + // Enforce max batch size (drop oldest if over limit) + if (debugLogQueue.messages.length > REMOTE_DEBUG_BATCH_MAX) { + debugLogQueue.messages.shift(); + debugLogQueue.droppedCount++; + } +} + +/** + * Submit queued debug logs to remote endpoint + * Uses 2-attempt retry, handles no_session error by disabling remote debug + */ +async function submitDebugLogs() { + if (!REMOTE_DEBUG_ENABLED || debugLogQueue.messages.length === 0) return; + if (debugLogQueue.isProcessing) return; // Prevent concurrent flushes + + debugLogQueue.isProcessing = true; + + // Include dropped count in this batch if any logs were dropped + if (debugLogQueue.droppedCount > 0) { + debugLogQueue.messages.push({ + date: Date.now(), + message: `[REMOTE DEBUG] ${debugLogQueue.droppedCount} logs dropped due to rate limiting` + }); + debugLogQueue.droppedCount = 0; + } + + // Take messages for submission + const messagesToSend = debugLogQueue.messages.slice(); + debugLogQueue.messages = []; + + const payload = { + debugkey: REMOTE_DEBUG_KEY, + data: messagesToSend + }; + + // Attempt up to 2 times + for (let attempt = 1; attempt <= 2; attempt++) { + try { + const response = await fetch(REMOTE_DEBUG_ENDPOINT, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + const result = await response.json(); + + // Check for no_session error + if (!result.success && result.reason === 'no_session') { + REMOTE_DEBUG_ENABLED = false; + // Stop timers + if (debugLogQueue.flushTimerId) { + clearInterval(debugLogQueue.flushTimerId); + debugLogQueue.flushTimerId = null; + } + if (debugLogQueue.rateResetTimerId) { + clearInterval(debugLogQueue.rateResetTimerId); + debugLogQueue.rateResetTimerId = null; + } + // Show error to user + originalConsoleError('[REMOTE DEBUG] Session not found - remote debugging disabled:', result.message); + // Don't retry on no_session + break; + } + + // Success + if (result.success) { + break; // Exit retry loop + } + + // Other error - will retry if attempt < 2 + originalConsoleWarn(`[REMOTE DEBUG] Submit attempt ${attempt} failed:`, result.reason || 'unknown'); + + } catch (err) { + originalConsoleWarn(`[REMOTE DEBUG] Submit attempt ${attempt} network error:`, err.message); + // Will retry if attempt < 2 + } + + // Wait 1 second before retry + if (attempt < 2) { + await new Promise(resolve => setTimeout(resolve, 1000)); + } + } + + debugLogQueue.isProcessing = false; +} + +/** + * Start remote debug timers (15s flush, 1s rate reset) + * Called only if REMOTE_DEBUG_ENABLED is true + */ +function startRemoteDebugTimers() { + if (!REMOTE_DEBUG_ENABLED) return; + + // 15-second flush timer + debugLogQueue.flushTimerId = setInterval(() => { + submitDebugLogs().catch(err => { + originalConsoleError('[REMOTE DEBUG] Flush error:', err.message); + }); + }, REMOTE_DEBUG_FLUSH_INTERVAL_MS); + + // 1-second rate limit reset timer + debugLogQueue.rateResetTimerId = setInterval(() => { + debugLogQueue.logsThisSecond = 0; + }, REMOTE_DEBUG_RATE_RESET_MS); + + originalConsoleLog('[REMOTE DEBUG] Remote debug logging enabled - logs will be sent to server every 15s'); +} + +// Override console methods to capture all output for remote debug +// These overrides call the original method AND queue for remote submission +console.log = function(...args) { + originalConsoleLog(...args); + queueRemoteDebugLog('log', args); +}; + +console.warn = function(...args) { + originalConsoleWarn(...args); + queueRemoteDebugLog('warn', args); +}; + +console.error = function(...args) { + originalConsoleError(...args); + queueRemoteDebugLog('error', args); +}; + +// Start remote debug timers if enabled +if (REMOTE_DEBUG_ENABLED) { + startRemoteDebugTimers(); + + // Register beforeunload to attempt final flush + window.addEventListener('beforeunload', () => { + if (REMOTE_DEBUG_ENABLED && debugLogQueue.messages.length > 0) { + // Use sendBeacon for reliable delivery during page unload + const payload = JSON.stringify({ + debugkey: REMOTE_DEBUG_KEY, + data: debugLogQueue.messages + }); + navigator.sendBeacon(REMOTE_DEBUG_ENDPOINT, payload); + } + }); +} + // Debug logging helper function function debugLog(message, ...args) { if (DEBUG_ENABLED) { From d2e45bf5cf4cc77138d1e481d6b16376e0bafcd1 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 11:54:56 -0500 Subject: [PATCH 03/11] Fix debug logging parameter to use '1' instead of 'true' --- content/wardrive.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index 168ffe0..3f85a84 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -8,9 +8,9 @@ import { WebBleConnection, Constants, Packet, BufferUtils } from "./mc/index.js"; // your BLE client // ---- Debug Configuration ---- -// Enable debug logging via URL parameter (?debug=true) or set default here +// Enable debug logging via URL parameter (?debug=1) or set default here const urlParams = new URLSearchParams(window.location.search); -const DEBUG_ENABLED = urlParams.get('debug') === 'true' || false; // Set to true to enable debug logging by default +const DEBUG_ENABLED = urlParams.get('debug') === '1' || false; // Set to true to enable debug logging by default // ---- Remote Debug Configuration ---- // Enable remote debug logging via URL parameters (?debuguser=1&debugkey=) From dcad59fbc1d09e1195f9b1074b427acce1e4b57a Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 11:58:47 -0500 Subject: [PATCH 04/11] Increase remote debug logging rate limit from 10 to 30 logs per second --- content/wardrive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index 3f85a84..ab2186a 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -23,7 +23,7 @@ let REMOTE_DEBUG_ENABLED = REMOTE_DEBUG_USER && REMOTE_DEBUG_KEY; // Can be disa const REMOTE_DEBUG_ENDPOINT = 'https://meshmapper.net/livedebug.php'; const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds -const REMOTE_DEBUG_RATE_LIMIT = 10; // Max logs per second +const REMOTE_DEBUG_RATE_LIMIT = 30; // Max logs per second const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval const debugLogQueue = { From 1726104c7f3f40dbe16f3336d154d62b762684d8 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:03:40 -0500 Subject: [PATCH 05/11] Remove debug prefix from console log messages in debug logging functions --- content/wardrive.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index ab2186a..3ebe7c1 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -227,19 +227,19 @@ if (REMOTE_DEBUG_ENABLED) { // Debug logging helper function function debugLog(message, ...args) { if (DEBUG_ENABLED) { - console.log(`[DEBUG] ${message}`, ...args); + console.log(message, ...args); } } function debugWarn(message, ...args) { if (DEBUG_ENABLED) { - console.warn(`[DEBUG] ${message}`, ...args); + console.warn(message, ...args); } } function debugError(message, ...args) { if (DEBUG_ENABLED) { - console.error(`[DEBUG] ${message}`, ...args); + console.error(message, ...args); // Also add to Error Log UI (use try-catch to prevent recursive errors) try { From 06f4f92abe41d5f869008d1aad53f9c5def2b1a1 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:26:01 -0500 Subject: [PATCH 06/11] Adjust remote debug logging rate limit to 10 logs per second and implement a grace period of 15 seconds before rate limiting starts --- content/wardrive.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index 3ebe7c1..ecd7d31 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -23,8 +23,9 @@ let REMOTE_DEBUG_ENABLED = REMOTE_DEBUG_USER && REMOTE_DEBUG_KEY; // Can be disa const REMOTE_DEBUG_ENDPOINT = 'https://meshmapper.net/livedebug.php'; const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds -const REMOTE_DEBUG_RATE_LIMIT = 30; // Max logs per second +const REMOTE_DEBUG_RATE_LIMIT = 10; // Max logs per second const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval +const REMOTE_DEBUG_GRACE_PERIOD_MS = 15000; // Grace period before rate limiting starts (15 seconds) const debugLogQueue = { messages: [], // Array of {date: , message: } @@ -32,7 +33,8 @@ const debugLogQueue = { rateResetTimerId: null, // Timer ID for rate limit reset logsThisSecond: 0, // Current rate counter droppedCount: 0, // Logs dropped due to rate limiting - isProcessing: false // Lock to prevent concurrent flush + isProcessing: false, // Lock to prevent concurrent flush + startupTimestamp: Date.now() // App launch time for grace period tracking }; // Store original console methods before overriding @@ -49,12 +51,19 @@ const originalConsoleError = console.error.bind(console); function queueRemoteDebugLog(level, args) { if (!REMOTE_DEBUG_ENABLED) return; - // Rate limiting check - if (debugLogQueue.logsThisSecond >= REMOTE_DEBUG_RATE_LIMIT) { + // Grace period check - bypass rate limiting for first 15 seconds + const gracePeriodActive = (Date.now() - debugLogQueue.startupTimestamp) < REMOTE_DEBUG_GRACE_PERIOD_MS; + + // Rate limiting check (only after grace period) + if (!gracePeriodActive && debugLogQueue.logsThisSecond >= REMOTE_DEBUG_RATE_LIMIT) { debugLogQueue.droppedCount++; return; // Drop this log } - debugLogQueue.logsThisSecond++; + + // Increment counter only after grace period + if (!gracePeriodActive) { + debugLogQueue.logsThisSecond++; + } // Serialize arguments to string const messageParts = args.map(arg => { @@ -185,6 +194,12 @@ function startRemoteDebugTimers() { // 1-second rate limit reset timer debugLogQueue.rateResetTimerId = setInterval(() => { debugLogQueue.logsThisSecond = 0; + + // Log when grace period expires (once at 15 seconds) + const elapsed = Date.now() - debugLogQueue.startupTimestamp; + if (elapsed >= REMOTE_DEBUG_GRACE_PERIOD_MS && elapsed < (REMOTE_DEBUG_GRACE_PERIOD_MS + REMOTE_DEBUG_RATE_RESET_MS)) { + originalConsoleLog('[REMOTE DEBUG] Grace period ended - rate limiting now active (30 logs/sec)'); + } }, REMOTE_DEBUG_RATE_RESET_MS); originalConsoleLog('[REMOTE DEBUG] Remote debug logging enabled - logs will be sent to server every 15s'); @@ -226,19 +241,19 @@ if (REMOTE_DEBUG_ENABLED) { // Debug logging helper function function debugLog(message, ...args) { - if (DEBUG_ENABLED) { + if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { console.log(message, ...args); } } function debugWarn(message, ...args) { - if (DEBUG_ENABLED) { + if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { console.warn(message, ...args); } } function debugError(message, ...args) { - if (DEBUG_ENABLED) { + if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { console.error(message, ...args); // Also add to Error Log UI (use try-catch to prevent recursive errors) From 324df24cf991b96f2b552fa3c9169dfed9838003 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:29:13 -0500 Subject: [PATCH 07/11] Update remote debug logging message to reflect current rate limit dynamically --- content/wardrive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index ecd7d31..f796994 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -198,7 +198,7 @@ function startRemoteDebugTimers() { // Log when grace period expires (once at 15 seconds) const elapsed = Date.now() - debugLogQueue.startupTimestamp; if (elapsed >= REMOTE_DEBUG_GRACE_PERIOD_MS && elapsed < (REMOTE_DEBUG_GRACE_PERIOD_MS + REMOTE_DEBUG_RATE_RESET_MS)) { - originalConsoleLog('[REMOTE DEBUG] Grace period ended - rate limiting now active (30 logs/sec)'); + originalConsoleLog(`[REMOTE DEBUG] Grace period ended - rate limiting now active (${REMOTE_DEBUG_RATE_LIMIT} logs/sec)`); } }, REMOTE_DEBUG_RATE_RESET_MS); From 8e92343bb3cdea6baa882d2ff2e1690de68bd174 Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:29:34 -0500 Subject: [PATCH 08/11] Reduce remote debug logging grace period from 15 seconds to 5 seconds --- content/wardrive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index f796994..a91bb1a 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -25,7 +25,7 @@ const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds const REMOTE_DEBUG_RATE_LIMIT = 10; // Max logs per second const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval -const REMOTE_DEBUG_GRACE_PERIOD_MS = 15000; // Grace period before rate limiting starts (15 seconds) +const REMOTE_DEBUG_GRACE_PERIOD_MS = 5000; // Grace period before rate limiting starts (15 seconds) const debugLogQueue = { messages: [], // Array of {date: , message: } From ca269e2d35e8effe1ef9467cc232272af8baad4d Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:32:47 -0500 Subject: [PATCH 09/11] Increase remote debug logging rate limit from 10 to 20 logs per second --- content/wardrive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index a91bb1a..478bb73 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -23,7 +23,7 @@ let REMOTE_DEBUG_ENABLED = REMOTE_DEBUG_USER && REMOTE_DEBUG_KEY; // Can be disa const REMOTE_DEBUG_ENDPOINT = 'https://meshmapper.net/livedebug.php'; const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds -const REMOTE_DEBUG_RATE_LIMIT = 10; // Max logs per second +const REMOTE_DEBUG_RATE_LIMIT = 20; // Max logs per second const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval const REMOTE_DEBUG_GRACE_PERIOD_MS = 5000; // Grace period before rate limiting starts (15 seconds) From 1e3e5247a9b44331f44ef7141dd36c93189895eb Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:34:20 -0500 Subject: [PATCH 10/11] Increase remote debug logging grace period from 5 seconds to 10 seconds --- content/wardrive.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/wardrive.js b/content/wardrive.js index 478bb73..782ad5b 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -25,7 +25,7 @@ const REMOTE_DEBUG_BATCH_MAX = 100; // Maximum logs per batch const REMOTE_DEBUG_FLUSH_INTERVAL_MS = 15000; // Flush every 15 seconds const REMOTE_DEBUG_RATE_LIMIT = 20; // Max logs per second const REMOTE_DEBUG_RATE_RESET_MS = 1000; // Rate limit reset interval -const REMOTE_DEBUG_GRACE_PERIOD_MS = 5000; // Grace period before rate limiting starts (15 seconds) +const REMOTE_DEBUG_GRACE_PERIOD_MS = 10000; // Grace period before rate limiting starts (15 seconds) const debugLogQueue = { messages: [], // Array of {date: , message: } From 628234142010294bb81e8853990463030b33792c Mon Sep 17 00:00:00 2001 From: MrAlders0n Date: Sat, 10 Jan 2026 12:41:39 -0500 Subject: [PATCH 11/11] Implement remote-only debug logging mode and bypass console output --- content/wardrive.js | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/content/wardrive.js b/content/wardrive.js index 782ad5b..de673e9 100644 --- a/content/wardrive.js +++ b/content/wardrive.js @@ -241,19 +241,53 @@ if (REMOTE_DEBUG_ENABLED) { // Debug logging helper function function debugLog(message, ...args) { - if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { + // Direct queue for remote-only mode (bypasses console) + if (REMOTE_DEBUG_ENABLED && !DEBUG_ENABLED) { + queueRemoteDebugLog('log', [message, ...args]); + return; // Don't proceed to console + } + + // Console output (which gets captured by override if remote is enabled) + if (DEBUG_ENABLED) { console.log(message, ...args); } } function debugWarn(message, ...args) { - if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { + // Direct queue for remote-only mode (bypasses console) + if (REMOTE_DEBUG_ENABLED && !DEBUG_ENABLED) { + queueRemoteDebugLog('warn', [message, ...args]); + return; // Don't proceed to console + } + + // Console output (which gets captured by override if remote is enabled) + if (DEBUG_ENABLED) { console.warn(message, ...args); } } function debugError(message, ...args) { - if (DEBUG_ENABLED || REMOTE_DEBUG_ENABLED) { + // Direct queue for remote-only mode (bypasses console) + if (REMOTE_DEBUG_ENABLED && !DEBUG_ENABLED) { + queueRemoteDebugLog('error', [message, ...args]); + + // Still add to Error Log UI even in remote-only mode + try { + const tagMatch = message.match(/^\[([^\]]+)\]/); + const source = tagMatch ? tagMatch[1] : null; + const cleanMessage = tagMatch ? message.replace(/^\[[^\]]+\]\s*/, '') : message; + + if (typeof addErrorLogEntry === 'function') { + addErrorLogEntry(cleanMessage, source); + } + } catch (e) { + // Silently fail to prevent recursive errors + } + return; // Don't proceed to console + } + + // Console output (which gets captured by override if remote is enabled) + if (DEBUG_ENABLED) { console.error(message, ...args); // Also add to Error Log UI (use try-catch to prevent recursive errors)