Improve quick scan error handling and user feedback

Frontend (wifi.js):
- Show helpful message when quick scan returns no networks
- Suggest using Deep Scan as fallback
- Better error messages with actionable suggestions

Backend (scanner.py):
- Add proper error messages from airport scan failures
- Add proper error messages from nmcli scan failures
- Handle timeouts and missing tools explicitly
- Raise RuntimeError with descriptive messages

These changes help users understand when quick scan tools (airport/nmcli)
aren't working and guide them to use Deep Scan instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-21 22:48:25 +00:00
parent d929c30882
commit 45c10a8593
2 changed files with 56 additions and 28 deletions
+13 -1
View File
@@ -302,6 +302,18 @@ const WiFiMode = (function() {
const result = await response.json();
console.log('[WiFiMode] Quick scan complete:', result);
// Check if we got results
if (!result.access_points || result.access_points.length === 0) {
if (result.error) {
throw new Error(result.error);
}
// No error but no results - might need different tool
console.warn('[WiFiMode] Quick scan returned no networks. Try Deep Scan instead.');
showError('Quick scan found no networks. System tools may not be available. Try Deep Scan with monitor mode.');
setScanning(false);
return;
}
// Process results
processQuickScanResult(result);
@@ -312,7 +324,7 @@ const WiFiMode = (function() {
}
} catch (error) {
console.error('[WiFiMode] Quick scan error:', error);
showError(error.message);
showError(error.message + '. Try using Deep Scan instead.');
setScanning(false);
}
}
+43 -27
View File
@@ -355,43 +355,59 @@ class UnifiedWiFiScanner:
airport_path = '/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport'
result = subprocess.run(
[airport_path, '-s'],
capture_output=True,
text=True,
timeout=timeout,
)
try:
result = subprocess.run(
[airport_path, '-s'],
capture_output=True,
text=True,
timeout=timeout,
)
if result.returncode != 0:
logger.warning(f"airport scan failed: {result.stderr}")
return []
if result.returncode != 0:
error_msg = result.stderr.strip() or f"airport returned code {result.returncode}"
logger.warning(f"airport scan failed: {error_msg}")
raise RuntimeError(f"airport scan failed: {error_msg}")
return parse_airport_scan(result.stdout)
if not result.stdout.strip():
logger.warning("airport returned empty output")
return []
return parse_airport_scan(result.stdout)
except subprocess.TimeoutExpired:
raise RuntimeError(f"airport scan timed out after {timeout}s")
except FileNotFoundError:
raise RuntimeError("airport utility not found")
def _scan_with_nmcli(self, interface: str, timeout: float) -> list[WiFiObservation]:
"""Scan using NetworkManager nmcli."""
from .parsers.nmcli import parse_nmcli_scan
# Trigger a rescan first
subprocess.run(
['nmcli', 'device', 'wifi', 'rescan', 'ifname', interface],
capture_output=True,
timeout=timeout / 2,
)
try:
# Trigger a rescan first
subprocess.run(
['nmcli', 'device', 'wifi', 'rescan', 'ifname', interface],
capture_output=True,
timeout=timeout / 2,
)
# Get results
result = subprocess.run(
['nmcli', '-t', '-f', 'BSSID,SSID,MODE,CHAN,FREQ,RATE,SIGNAL,SECURITY', 'device', 'wifi', 'list', 'ifname', interface],
capture_output=True,
text=True,
timeout=timeout,
)
# Get results
result = subprocess.run(
['nmcli', '-t', '-f', 'BSSID,SSID,MODE,CHAN,FREQ,RATE,SIGNAL,SECURITY', 'device', 'wifi', 'list', 'ifname', interface],
capture_output=True,
text=True,
timeout=timeout,
)
if result.returncode != 0:
logger.warning(f"nmcli scan failed: {result.stderr}")
return []
if result.returncode != 0:
error_msg = result.stderr.strip() or f"nmcli returned code {result.returncode}"
logger.warning(f"nmcli scan failed: {error_msg}")
raise RuntimeError(f"nmcli scan failed: {error_msg}")
return parse_nmcli_scan(result.stdout)
return parse_nmcli_scan(result.stdout)
except subprocess.TimeoutExpired:
raise RuntimeError(f"nmcli scan timed out after {timeout}s")
except FileNotFoundError:
raise RuntimeError("nmcli not found (NetworkManager not installed)")
def _scan_with_iw(self, interface: str, timeout: float) -> list[WiFiObservation]:
"""Scan using iw."""