fix: harden three unwrap/expect panic surfaces in request paths #108

Merged
YousifShkara merged 1 commit from fix/harden-unwrap-panic-surfaces into main 2026-06-11 05:43:50 +02:00
Owner

Closes BUNYIP-87.

Three call sites in request handlers used .unwrap() / .expect() where a future runtime condition could promote a soft failure into a worker panic. None of these fire today; the fixes preserve happy-path behaviour and replace each panic surface with the smallest safe alternative.

  1. bunyip-api/src/handlers/webhook.rs::stripe_webhook: the per-request tier_config.read().expect("TierConfig lock poisoned") panicked on every Stripe delivery after any panic in the tier-settings write path had poisoned the lock. Recover through PoisonError::into_inner() (safe here because this path only reads) and log a single error line so ops can grep for the upstream panic.

  2. bunyip-web/src/handlers/dashboard.rs::membership: the @else if has { ... } branch did @let m = current.clone().unwrap(); where has was driven by membership_status and current was loaded separately. Today they correlate, but a future API hiccup with a fresh enum but a missing membership row would panic the Maud thread. Couple the field access to a matching @if let Some(m) = current.clone() so a missing current simply renders nothing in this branch.

  3. bunyip-api/src/handlers/admin.rs::get_key_health: serde_json::to_value(&check).unwrap() is practically infallible for the current KeyHealthCheck struct but the contract leaks a panic surface that a future f64 field (NaN → Err) would expose. Fail-soft with a null entry plus a tracing::error! line; the /admin/key-health endpoint still returns 200 with the failing key marked null instead of 500-panicking the whole aggregator.

No behaviour change on the happy path. just check-container clean (modulo the pre-existing test_config_defaults parallel-env-var flake on main; 188 other tests pass; fmt + clippy + build clean).

#BUNYIP-87

Closes BUNYIP-87. Three call sites in request handlers used `.unwrap()` / `.expect()` where a future runtime condition could promote a soft failure into a worker panic. None of these fire today; the fixes preserve happy-path behaviour and replace each panic surface with the smallest safe alternative. 1. `bunyip-api/src/handlers/webhook.rs::stripe_webhook`: the per-request `tier_config.read().expect("TierConfig lock poisoned")` panicked on every Stripe delivery after any panic in the tier-settings write path had poisoned the lock. Recover through `PoisonError::into_inner()` (safe here because this path only reads) and log a single error line so ops can grep for the upstream panic. 2. `bunyip-web/src/handlers/dashboard.rs::membership`: the `@else if has { ... }` branch did `@let m = current.clone().unwrap();` where `has` was driven by `membership_status` and `current` was loaded separately. Today they correlate, but a future API hiccup with a fresh enum but a missing membership row would panic the Maud thread. Couple the field access to a matching `@if let Some(m) = current.clone()` so a missing `current` simply renders nothing in this branch. 3. `bunyip-api/src/handlers/admin.rs::get_key_health`: `serde_json::to_value(&check).unwrap()` is practically infallible for the current `KeyHealthCheck` struct but the contract leaks a panic surface that a future `f64` field (NaN → Err) would expose. Fail-soft with a null entry plus a tracing::error! line; the `/admin/key-health` endpoint still returns 200 with the failing key marked null instead of 500-panicking the whole aggregator. No behaviour change on the happy path. `just check-container` clean (modulo the pre-existing `test_config_defaults` parallel-env-var flake on `main`; 188 other tests pass; fmt + clippy + build clean). #BUNYIP-87
fix: harden three unwrap/expect panic surfaces in request paths
All checks were successful
Create release / Create release from merged PR (pull_request) Has been skipped
Check / fmt / clippy / build / test (pull_request) Successful in 2m12s
29939af90b
Closes BUNYIP-87.

Three call sites in request handlers used `.unwrap()` / `.expect()` where a future runtime condition could promote a soft failure into a worker panic. None of these fire today; the fixes preserve happy-path behaviour and replace each panic surface with the smallest safe alternative.

1. `bunyip-api/src/handlers/webhook.rs::stripe_webhook`: the per-request `tier_config.read().expect("TierConfig lock poisoned")` panicked on every Stripe delivery after any panic in the tier-settings write path had poisoned the lock. Recover through `PoisonError::into_inner()` (safe here because this path only reads) and log a single error line so ops can grep for the upstream panic.

2. `bunyip-web/src/handlers/dashboard.rs::membership`: the `@else if has { ... }` branch did `@let m = current.clone().unwrap();` where `has` was driven by `membership_status` and `current` was loaded separately. Today they correlate, but a future API hiccup with a fresh enum but a missing membership row would panic the Maud thread. Couple the field access to a matching `@if let Some(m) = current.clone()` so a missing `current` simply renders nothing in this branch.

3. `bunyip-api/src/handlers/admin.rs::get_key_health`: `serde_json::to_value(&check).unwrap()` is practically infallible for the current `KeyHealthCheck` struct but the contract leaks a panic surface that a future `f64` field (NaN → Err) would expose. Fail-soft with a null entry plus a tracing::error! line; the `/admin/key-health` endpoint still returns 200 with the failing key marked null instead of 500-panicking the whole aggregator.

No behaviour change on the happy path. `just check-container` clean (modulo the pre-existing `test_config_defaults` parallel-env-var flake on `main`; 188 other tests pass; fmt + clippy + build clean).

#BUNYIP-87
YousifShkara deleted branch fix/harden-unwrap-panic-surfaces 2026-06-11 05:43:50 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psa-systems/bunyip!108
No description provided.