Skip to content

fix: attach context to startup permission errors (#62) - #63

Merged
thedancingdeveloper merged 4 commits into
mainfrom
fix/62-startup-permission-error-context
Aug 10, 2026
Merged

fix: attach context to startup permission errors (#62)#63
thedancingdeveloper merged 4 commits into
mainfrom
fix/62-startup-permission-error-context

Conversation

@thedancingdeveloper

@thedancingdeveloper thedancingdeveloper commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two fixes for #62:

1. Startup errors now carry context. Two ?-propagated io::Errors had no .context(), so a permission failure crashed the process with nothing but a bare Permission denied (os error 13) — no path, no address, no hint. This matches the reporter's log verbatim.

  • crates/nzb-web/src/startup.rscreate_dir_all for data_dir/incomplete_dir/complete_dir now goes through a create_data_dir() helper that wraps the error with the failing path and a Docker-ownership hint (mirrors the existing pattern in AppConfig::load/save).
  • apps/rustnzb/src/server.rsTcpListener::bind in serve() now wraps the error with the bind address.

Reproduced locally before the fix: pointing data_dir at a path under a chmod 000 parent produced the anyhow {:?} output Permission denied (os error 13) — nothing else — confirming this is a real, reachable source of the reported crash text. (The other candidate, a privileged-port bind failure, is a secondary/unlikely cause here since the Docker image hardcoded --port 9090 — see below.)

2. RUSTNZB_PORT was dead in the Docker image. svc-rustnzb/run always passed --port 9090 explicitly, and clap gives an explicit CLI flag precedence over its env fallback, so RUSTNZB_PORT (and config.toml's general.port) were silently unconfigurable in Docker despite being documented as a general env var override. Removed the hardcoded flag so the env var (and config file) actually take effect; default when neither is set is unchanged (still 9090, matching the Dockerfile's EXPOSE and config.rs's default). Since the image's EXPOSE stays static at build time, documented that overriding RUSTNZB_PORT still needs a matching host-side port remap (-p host:$RUSTNZB_PORT).

This is a diagnosability + configuration fix, not a fix for the reporter's underlying Synology permission/ACL problem — it turns an untraceable bare crash into an actionable error, and makes the documented RUSTNZB_PORT behavior actually true. Still waiting on the reporter for their config values and full log to confirm which exact path fired; see issue comment: #62 (comment)

Test plan

  • cargo test -p nzb-web --lib — unit tests for create_data_dir: positive (nested dir creation succeeds) and negative (non-writable parent produces context-bearing error, #[cfg(unix)])
  • cargo test -p nzb-web --test startup_directory_errors — integration tests: positive (custom, non-default incomplete/complete/data dirs succeed), negative (regression guard asserting the error is not the bare Permission denied (os error 13) text from "Permission denied" in Synology container manager #62, and does contain the failing path)
  • cargo test -p rustnzb --test server_bind_error_test — integration tests: positive (bind to a free port succeeds), negative (bind to an already-occupied address fails with context naming the address)
  • cargo test -p rustnzb --bin rustnzb — regression tests locking in clap's --port/RUSTNZB_PORT precedence (env used when no flag; flag wins when both given; None when neither), so a future entrypoint change can't silently reintroduce the hardcoding bug
  • Full existing suites green: cargo test -p nzb-web --tests (69 unit + all integration), cargo test -p rustnzb --tests (all e2e/integration)
  • cargo fmt --check / cargo clippy --all-targets -- -D warnings clean on both crates

References #62.

🤖 Generated with Claude Code

Directory-creation and TCP-bind failures during startup propagated a
raw io::Error with a bare `?`, so anyhow printed nothing but
`Permission denied (os error 13)` — no path, no hint. Reproduced
locally: pointing data_dir at a non-writable parent produces that
exact bare text before the fix.

Wrap both with .with_context(), matching the existing pattern in
AppConfig::load/save, so failures name the path/address and (for
directory creation) hint at the Docker ownership fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
thedancingdeveloper and others added 3 commits August 10, 2026 09:12
svc-rustnzb/run always passed --port 9090 explicitly, and clap gives an
explicit CLI flag precedence over its env fallback, so RUSTNZB_PORT (and
config.toml's general.port) were silently unconfigurable in the Docker
image despite being documented as a general env var override. Default
behavior when neither is set is unchanged (still 9090, matching the
Dockerfile's EXPOSE and config.rs's default).

Since the image's EXPOSE stays static at build time, document that
overriding RUSTNZB_PORT still needs a matching host-side port remap.

Add regression tests locking in clap's precedence (env used when no
flag, flag wins when both given, None when neither) so a future
entrypoint change can't silently reintroduce this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI's self-hosted runner runs cargo test as root, which bypasses the
chmod 000 permission check entirely, so create_dir_all under a
"locked" parent unexpectedly succeeds and expect_err() panics.
Skip the assertions (rather than fail) when the operation
unexpectedly succeeds under elevated privileges, matching how these
tests already behave correctly for a non-root user.

Verified locally both as a regular user (real EACCES exercised) and
via sudo (early-return path exercised, no panic).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
container-smoke, release-image, and the container-canary workflow all
build the runtime Dockerfile from scratch, pulling alpine:3.23
unauthenticated. The shared self-hosted runner's IP hits Docker Hub's
anonymous pull rate limit (429) under normal CI load, unrelated to any
code change.

Log in to docker.io with the DOCKERHUB_TOKEN/DOCKERHUB_USERNAME
secrets (falls back to anonymous pulls if unset) before building,
mirroring the existing repo.indexarr.net/ghcr.io login pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@thedancingdeveloper
thedancingdeveloper merged commit ed880c9 into main Aug 10, 2026
6 checks passed
@thedancingdeveloper
thedancingdeveloper deleted the fix/62-startup-permission-error-context branch August 10, 2026 10:23
thedancingdeveloper added a commit that referenced this pull request Aug 10, 2026
Ships the startup diagnosability fix: permission and bind failures
during startup now attach the failing path/address instead of a bare
Permission denied (os error 13), plus the Docker RUSTNZB_PORT
hardcoding fix (#62, #63).

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant