Skip to content

fix: replace panic!() with graceful error handling - #223

Merged
nh13 merged 1 commit into
mainfrom
nh/panic-fixups
Apr 4, 2026
Merged

nh13 merged 1 commit into
mainfrom
nh/panic-fixups

Conversation

@nh13

@nh13 nh13 commented Apr 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Convert 10 production panic!() calls to anyhow::bail!() with proper Result propagation for input validation errors (non-string UMI tags, mismatched UMIs, malformed FASTQ records)
  • Replace 20 test panic!() calls with unreachable!() (let-else branches) or .unwrap_err() assertions (#[should_panic] tests)
  • Keep 2 production panic!() calls that guard programmer invariants (PipelineStep::from_index const fn, bounded queue post-is_full push)

Details

Production changes:

  • correct.rs: extract_and_validate_template_umi{,_raw} return Result<Option<String>> instead of panicking on malformed UMI data
  • fastq.rs: from_record_with_structure returns Result<Self>; ReadSetIterator yields Result<FastqSet> items
  • extract.rs: Updated callers to propagate errors via ? and .map_err(io::Error::other)

Test changes (8 files):

  • panic!() → unreachable!() in let-else fallback branches
  • #[should_panic] → .unwrap_err() + assert!(err.to_string().contains(...))
  • Removed redundant assert!(x.is_some()) before let-else patterns

Test plan

  • cargo ci-test — 2213 tests pass
  • cargo ci-fmt — clean
  • cargo ci-lint — clean
  • No remaining panic!() in production code except 2 intentional invariant guards

@nh13
nh13 temporarily deployed to github-actions April 4, 2026 06:05 — with GitHub Actions Inactive
@nh13
nh13 marked this pull request as ready for review April 4, 2026 06:06
@coderabbitai

coderabbitai Bot commented Apr 4, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@nh13 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 59 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 59 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9270ada6-de68-420c-99e9-80e9220d1c9c

📥 Commits

Reviewing files that changed from the base of the PR and between c958376 and 46ef276.

📒 Files selected for processing (10)
  • src/commands/compare/metrics.rs
  • src/commands/correct.rs
  • src/commands/dedup.rs
  • src/commands/extract.rs
  • src/lib/fastq.rs
  • src/lib/sort/raw_bam_reader.rs
  • src/lib/tag_reversal.rs
  • src/lib/template.rs
  • src/lib/umi/parallel_assigner.rs
  • src/lib/unified_pipeline/deadlock.rs
📝 Walkthrough

Walkthrough

The pull request refactors error handling across multiple modules. Function signatures in src/commands/correct.rs change to return anyhow::Result<Option<String>> instead of Option<String>, with panic-based failures replaced by error propagation. FastqSet::from_record_with_structure in src/lib/fastq.rs similarly returns anyhow::Result<FastqSet>. Unit tests are updated to remove #[should_panic] attributes and instead assert errors via unwrap_err() with message checks. Test assertion patterns are simplified across multiple files using let ... else { unreachable!(...) } syntax in place of explicit panic branches, while preserving expected validation behavior.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately summarizes the main change: converting panic!() calls to graceful error handling across the codebase.
Description check ✅ Passed Description is directly related to the changeset, detailing production panic-to-Result conversions, test refactorings, and verification steps.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch nh/panic-fixups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nh13 nh13 added the hygiene label Apr 4, 2026
@codecov

codecov Bot commented Apr 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.65957% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.36%. Comparing base (0c6b0ae) to head (46ef276).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/commands/correct.rs 82.60% 4 Missing ⚠️
src/commands/compare/metrics.rs 66.66% 3 Missing ⚠️
src/lib/fastq.rs 84.21% 3 Missing ⚠️
src/lib/umi/parallel_assigner.rs 0.00% 3 Missing ⚠️
src/commands/dedup.rs 66.66% 2 Missing ⚠️
src/lib/template.rs 66.66% 2 Missing ⚠️
src/lib/unified_pipeline/deadlock.rs 66.66% 2 Missing ⚠️
src/commands/extract.rs 93.75% 1 Missing ⚠️
src/lib/tag_reversal.rs 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #223      +/-   ##
==========================================
+ Coverage   88.27%   88.36%   +0.08%     
==========================================
  Files         113      113              
  Lines       53215    53521     +306     
==========================================
+ Hits        46977    47292     +315     
+ Misses       6238     6229       -9     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@nh13

nh13 commented Apr 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 4, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/commands/correct.rs (1)

776-780: Consider documenting truncated record behavior.

Returning Ok(None) for records < 32 bytes treats corruption as "missing UMI." This is defensive but the doc comment (lines 760-766) doesn't mention it.

📝 Suggested doc update
     /// # Errors
     ///
     /// Returns an error if:
     /// - Records have different UMIs
     /// - Some records have UMIs and others don't
     /// - UMI tag has non-string type
+    ///
+    /// Note: Truncated records (< 32 bytes) are treated as having no UMI.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/correct.rs` around lines 776 - 780, Update the doc comment above
the function (the block around lines 760-766) to describe that truncated records
(any entry in raw_records with length < 32) are treated as corrupted and cause
the function to return Ok(None) (i.e., treated as a "missing UMI"); reference
the guard that checks raw_records.iter().any(|r| r.len() < 32) and the early
return Ok(None) so callers know this defensive behavior and its rationale.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/extract.rs`:
- Around line 901-905: The loop currently treats a None from iter.next() as
end-of-reads and breaks, which silently accepts asymmetric EOFs; change the None
arm so that if any reader's iterator yields None while others still produce
reads you return an out-of-sync error instead of breaking. Concretely, in the
block handling iter.next() (the match that pushes into next_read_sets using iter
and next_read_sets), replace the None => break behavior with returning an
appropriate error (e.g., an OutOfSync/Io error or your crate's sync error) that
includes context about which reader/index went EOF, ensuring callers can detect
FASTQ truncation rather than proceeding silently.

---

Nitpick comments:
In `@src/commands/correct.rs`:
- Around line 776-780: Update the doc comment above the function (the block
around lines 760-766) to describe that truncated records (any entry in
raw_records with length < 32) are treated as corrupted and cause the function to
return Ok(None) (i.e., treated as a "missing UMI"); reference the guard that
checks raw_records.iter().any(|r| r.len() < 32) and the early return Ok(None) so
callers know this defensive behavior and its rationale.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e931af75-3013-42c5-8312-c524a9bb1f1e

📥 Commits

Reviewing files that changed from the base of the PR and between ea6e3fc and c958376.

📒 Files selected for processing (10)
  • src/commands/compare/metrics.rs
  • src/commands/correct.rs
  • src/commands/dedup.rs
  • src/commands/extract.rs
  • src/lib/fastq.rs
  • src/lib/sort/raw_bam_reader.rs
  • src/lib/tag_reversal.rs
  • src/lib/template.rs
  • src/lib/umi/parallel_assigner.rs
  • src/lib/unified_pipeline/deadlock.rs

Comment thread src/commands/extract.rs
Convert 10 production panic!() calls to anyhow::bail!() with proper
Result propagation for user-input validation errors in correct and
extract commands. Replace 18 test panic!() calls with unreachable!()
or error assertions for idiomatic Rust test patterns.

- correct.rs: extract_and_validate_template_umi{,_raw} now return
  Result<Option<String>> instead of panicking on malformed UMI data
- fastq.rs: from_record_with_structure returns Result<Self>;
  ReadSetIterator yields Result<FastqSet> items
- extract.rs: updated callers to propagate errors
- Test files: panic!() → unreachable!(), #[should_panic] → error checks
@nh13
nh13 force-pushed the nh/panic-fixups branch from c958376 to 46ef276 Compare April 4, 2026 20:44
@nh13
nh13 temporarily deployed to github-actions April 4, 2026 20:44 — with GitHub Actions Inactive
@nh13
nh13 merged commit ce3bcf3 into main Apr 4, 2026
7 of 8 checks passed
@nh13
nh13 deleted the nh/panic-fixups branch April 4, 2026 20:47
@nh13 nh13 mentioned this pull request Apr 4, 2026

This branch was previously deployed

1 inactive deployment
github-actions — 46ef276c Deployed Apr 4, 2026 by nh13 via coverage #883
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant