diff --git a/frontend/index.html b/frontend/index.html
index 25392c0..840e52a 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2546,15 +2546,22 @@ async function startCapture() {
}
}
-async function stopCapture() {
+// Stopping when nothing is capturing answers 400 ('No active capture'), and
+// 403 when capture is disabled — both mean "there is no capture to stop",
+// not a failure.
+async function stopCaptureIfRunning() {
try {
await API.stopCapture();
- STATE.captureActive = false;
- STATE.currentSessionId = null;
- STATE.sessionViewId = null;
- updateCaptureButton();
- updateStatus();
- } catch (e) {}
+ } catch (e) { /* nothing to stop */ }
+}
+
+async function stopCapture() {
+ await stopCaptureIfRunning();
+ STATE.captureActive = false;
+ STATE.currentSessionId = null;
+ STATE.sessionViewId = null;
+ updateCaptureButton();
+ updateStatus();
}
function updateCaptureButton() {
@@ -2891,8 +2898,9 @@ async function confirmComprionImport() {
async function loadSession(id) {
if (STATE.captureActive && !confirm(t('Opening a session stops the active capture. Continue?'))) return;
try {
- // Stop any active capture so it can't write into the session being viewed.
- await API.stopCapture();
+ // Stop any active capture so it can't write into the session being
+ // viewed (a 400/403 means there is none — not an error).
+ await stopCaptureIfRunning();
STATE.captureActive = false;
STATE.currentSessionId = null;
stopPolling();
@@ -3164,7 +3172,7 @@ function onFilterChange() {
}
// ──────────────────── Versioning ────────────────────
-const PWA_VERSION = '2.4.0';
+const PWA_VERSION = '2.4.1';
const MIN_SERVER_VERSION = '2.3.0';
function parseVersion(v) {
diff --git a/frontend/sw.js b/frontend/sw.js
index 6c14a72..46b983c 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simtrace-analyser-v65';
+const CACHE = 'simtrace-analyser-v66';
const PRECACHE = [
'index.html',
diff --git a/frontend/tests/comprion.test.js b/frontend/tests/comprion.test.js
index 194a7e1..325ca2b 100644
--- a/frontend/tests/comprion.test.js
+++ b/frontend/tests/comprion.test.js
@@ -498,3 +498,29 @@ test('Change details modal: header, summary, legend and both views', () => {
assert.match(el('file-diff-sub').textContent, /Record 2 \/ 5/);
assert.match(el('file-diff-body').innerHTML, /image is frozen/);
});
+
+// ── Session loading must survive "no active capture" ───────────────────
+eval(extractFunc(src, 'stopCaptureIfRunning'));
+
+test('stopCaptureIfRunning tolerates "no active capture" (400)', async () => {
+ let calls = 0;
+ API.stopCapture = async () => {
+ calls++;
+ throw Object.assign(new Error('No active capture'), {status: 400});
+ };
+ await stopCaptureIfRunning(); // must resolve, not reject
+ assert.equal(calls, 1);
+ API.stopCapture = async () => { calls++; };
+ await stopCaptureIfRunning();
+ assert.equal(calls, 2);
+});
+
+test('loadSession stops a capture tolerantly', () => {
+ const body = extractFunc(src, 'loadSession');
+ assert.match(body, /await stopCaptureIfRunning\(\)/);
+ assert.doesNotMatch(body, /await API\.stopCapture\(\)/);
+ // The capture button also resets its state after a tolerated stop.
+ const stop = extractFunc(src, 'stopCapture');
+ assert.match(stop, /await stopCaptureIfRunning\(\)/);
+ assert.match(stop, /STATE\.captureActive = false;/);
+});
diff --git a/pyproject.toml b/pyproject.toml
index b4bf5d7..41276e8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "simtrace-analyser"
-version = "2.4.0"
+version = "2.4.1"
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"}
diff --git a/simtrace_analyser/version.py b/simtrace_analyser/version.py
index 2c9091b..e756343 100644
--- a/simtrace_analyser/version.py
+++ b/simtrace_analyser/version.py
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version."""
-__version__ = '2.4.0'
+__version__ = '2.4.1'
diff --git a/tests/test_pcap_import.py b/tests/test_pcap_import.py
index 62e8dc7..13748aa 100644
--- a/tests/test_pcap_import.py
+++ b/tests/test_pcap_import.py
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase):
def test_package_version(self):
from simtrace2_pysniff import __version__
- self.assertEqual(__version__, '2.4.0')
+ self.assertEqual(__version__, '2.4.1')
def test_status_includes_version(self):
import os