From 727e20e69b0a0dea0938888279fa2c123c84ebca Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 16 Jul 2026 21:08:05 -0600 Subject: [PATCH 1/2] docs: Add gotcha for fatal startup exit status --- memory-bank/decisions/adr-0001-known-gotchas.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/memory-bank/decisions/adr-0001-known-gotchas.md b/memory-bank/decisions/adr-0001-known-gotchas.md index b42f70394..cbf55f24f 100644 --- a/memory-bank/decisions/adr-0001-known-gotchas.md +++ b/memory-bank/decisions/adr-0001-known-gotchas.md @@ -21988,3 +21988,20 @@ push and release tag then produced a failed pipeline before any job could run. their shell text contains `: ` or other YAML-significant syntax. Validate the full file with the target GitLab instance's CI lint API; a generic YAML parser cannot detect GitLab's required string shape. + +### 0z562. Fatal Startup Exceptions Must Exit With A Failure Status + +**The Bug**: `StartupWebApplicationRunner.Run(...)` caught unexpected startup +exceptions, logged `Application terminated unexpectedly`, and then returned +without calling the injected process-exit action. Fatal failures such as an +occupied HTTP port therefore produced exit status `0`, so service managers and +deployment probes treated a failed start as success. + +**Files Affected**: +- `src/slskd/Bootstrap/StartupWebApplicationRunner.cs` +- `tests/slskd.Tests.Unit/Bootstrap/StartupWebApplicationRunnerTests.cs` + +**Prevention**: Every startup path that catches a fatal exception must call the +injected exit action with a non-zero status. Keep a unit assertion on the +generic termination handler and a live occupied-listener check so fatal logs +cannot be paired with a successful process status. From 3a494b86fdf6cde17e08faf7a42570d59cd2af01 Mon Sep 17 00:00:00 2001 From: Keith Date: Thu, 16 Jul 2026 21:09:46 -0600 Subject: [PATCH 2/2] fix fatal startup exit status --- docs/CHANGELOG.md | 3 +++ memory-bank/activeContext.md | 12 +++++++++ memory-bank/progress.md | 2 ++ memory-bank/tasks.md | 1 + .../Bootstrap/StartupWebApplicationRunner.cs | 11 +++++++- .../StartupWebApplicationRunnerTests.cs | 26 +++++++++++++++++++ 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 tests/slskd.Tests.Unit/Bootstrap/StartupWebApplicationRunnerTests.cs diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8860e73d9..e0ada1bf4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,6 +22,9 @@ For dev or build tags, use the same logical version string embedded in the tag. ## [Unreleased] +- Unexpected startup failures now return a non-zero process status after fatal + logging, so service managers no longer treat an occupied HTTP port as a + successful start. - The web footer and README now offer direct PayPal and Ko-fi links for supporting slskdN development. - GitLab CI configuration now keeps the Arch package-smoke sudoers command as diff --git a/memory-bank/activeContext.md b/memory-bank/activeContext.md index 21d1d80b2..fd83c806a 100644 --- a/memory-bank/activeContext.md +++ b/memory-bank/activeContext.md @@ -9122,3 +9122,15 @@ rollback. - Passed backend and Web production builds plus focused core-page Playwright coverage (`4/4`). - Next steps: 1. Push the CI fixes to `main` without creating a release/build tag. + +## Update 2026-07-17 03:15:00Z + +- Current task: fatal startup exit-status fix ready for draft PR. +- Last activity: + - Changed unexpected startup termination to return process status `1` after logging the fatal exception. + - Added focused regression coverage and documented gotcha `0z562`. +- Validation: + - Passed the focused unit test and a live occupied-HTTP-port process check (`exit=1`). +- Next steps: + 1. Review and merge the draft PR. + 2. No release/build tag was created. diff --git a/memory-bank/progress.md b/memory-bank/progress.md index a9236f39e..8973466e9 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -10781,3 +10781,5 @@ Code quality improvements were completed as part of Option A: [2026-07-15T01:30:00Z] GitLab reconciliation follow-up: preserved both GitHub and GitLab histories with a non-destructive merge because GitLab's unique libclang commits were already represented on current main. Included concurrent Prometheus metadata hardening so malformed HELP/TYPE lines are skipped rather than failing the metrics response. Focused Prometheus parsing tests passed (`6/6`). [2026-07-15T22:55:00Z] CI repair: regenerated `docs/system-surfaces-current.md` after the Streams share-ticket route changed, and corrected the E2E harness to create unique Web content directories relative to the built executable instead of the application-data directory. Focused core-page Playwright coverage passed (`4/4`) and backend/Web production builds completed successfully. No release tag was created. + +[2026-07-17T03:15:00Z] Startup exit-status repair: unexpected exceptions caught by `StartupWebApplicationRunner` now invoke the injected exit action with status `1` after fatal logging. Added a focused unit regression and documented gotcha `0z562`. Validation passed with the focused test and a live occupied-HTTP-port process check (`exit=1`). No release tag was created. diff --git a/memory-bank/tasks.md b/memory-bank/tasks.md index e411fd280..022a75dd7 100644 --- a/memory-bank/tasks.md +++ b/memory-bank/tasks.md @@ -3056,3 +3056,4 @@ - [2026-07-15T01:15:00Z] Completed: integrate and validate Dependabot PRs #257, #263, and #264, including direct test-package alignment for grouped NuGet updates. - [2026-07-15T01:30:00Z] Completed: harden Prometheus metadata parsing against malformed custom collector lines. - [2026-07-15T22:55:00Z] Completed: refresh the API route inventory and fix E2E Web content resolution with isolated executable-relative directories. +- [2026-07-17T03:15:00Z] Completed: make unexpected startup failures return a non-zero process status, with unit and occupied-port process regression coverage. diff --git a/src/slskd/Bootstrap/StartupWebApplicationRunner.cs b/src/slskd/Bootstrap/StartupWebApplicationRunner.cs index 3a976f37c..bbe67ce36 100644 --- a/src/slskd/Bootstrap/StartupWebApplicationRunner.cs +++ b/src/slskd/Bootstrap/StartupWebApplicationRunner.cs @@ -117,11 +117,20 @@ public static void Run( } catch (Exception ex) { - log.Fatal(ex, "Application terminated unexpectedly"); + HandleUnexpectedTermination(ex, log, exit); } finally { Serilog.Log.CloseAndFlush(); } } + + internal static void HandleUnexpectedTermination( + Exception exception, + Serilog.ILogger log, + Action exit) + { + log.Fatal(exception, "Application terminated unexpectedly"); + exit(1); + } } diff --git a/tests/slskd.Tests.Unit/Bootstrap/StartupWebApplicationRunnerTests.cs b/tests/slskd.Tests.Unit/Bootstrap/StartupWebApplicationRunnerTests.cs new file mode 100644 index 000000000..6b91521ea --- /dev/null +++ b/tests/slskd.Tests.Unit/Bootstrap/StartupWebApplicationRunnerTests.cs @@ -0,0 +1,26 @@ +// +// Copyright (c) slskdN Team. All rights reserved. +// + +namespace slskd.Tests.Unit.Bootstrap; + +using Serilog; +using slskd.Bootstrap; +using Xunit; + +public sealed class StartupWebApplicationRunnerTests +{ + [Fact] + public void HandleUnexpectedTermination_ExitsWithFailureStatus() + { + int? exitCode = null; + using var log = new LoggerConfiguration().CreateLogger(); + + StartupWebApplicationRunner.HandleUnexpectedTermination( + new InvalidOperationException("startup failed"), + log, + code => exitCode = code); + + Assert.Equal(1, exitCode); + } +}