Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions memory-bank/activeContext.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
17 changes: 17 additions & 0 deletions memory-bank/decisions/adr-0001-known-gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 2 additions & 0 deletions memory-bank/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions memory-bank/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
11 changes: 10 additions & 1 deletion src/slskd/Bootstrap/StartupWebApplicationRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> exit)
{
log.Fatal(exception, "Application terminated unexpectedly");
exit(1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// <copyright file="StartupWebApplicationRunnerTests.cs" company="slskdN Team">
// Copyright (c) slskdN Team. All rights reserved.
// </copyright>

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);
}
}
Loading