Skip to content

feat(java): signal retry intent by exception type#500

Merged
pratyush618 merged 5 commits into
masterfrom
feat/java-typed-retry-exceptions
Jul 23, 2026
Merged

feat(java): signal retry intent by exception type#500
pratyush618 merged 5 commits into
masterfrom
feat/java-typed-retry-exceptions

Conversation

@kartikeya-27

@kartikeya-27 kartikeya-27 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Closes #484.

A Java handler could not say why it failed: everything thrown was classified the same way, and the only lever was the task-wide retryOn predicate added in #498.

Typed retry signals (Java)

RetryableException and NonRetryableException join org.byteveda.taskito.errors. A handler throws one to classify its own failure:

queue.worker().handle(CHARGE, (Order order) -> {
    Response response = gateway.charge(order);
    if (response.status() == 402) {
        throw new NonRetryableException("card declined");     // dead-letters now
    }
    if (response.status() >= 500) {
        throw new RetryableException("gateway " + response.status()); // spends the budget
    }
    return response.body();
});
  • Both beat the task's retryOn predicate — the throw site is more specific than a task-wide classifier.
  • Honoured through the cause chain (bounded walk, so a cyclic chain can't spin the worker thread); the outermost signal wins.
  • Classification moves into RetryDecision, so the bridge just asks it; unsignalled failures fall back to the predicate, and with no predicate everything still retries.

Tests: unit coverage of the classification rules, plus end-to-end runs asserting a permanent failure dead-letters after one attempt and a retryable one exhausts its budget even under a rejecting predicate.

Follow-ups from #499

Two review comments on the retention-echo PR were never actioned:

  • Reserved settings prefixes are now core-owned. taskito_core::RESERVED_SETTING_PREFIXES is the canonical list, exported by all three bindings; each shell's dashboard settings API derives its hidden-key list from it instead of hardcoding a slightly different one. Documented in BINDING_CONTRACT.md, with a per-shell test that the published retention document is neither listed nor writable.
  • An unparseable published retention document is logged. It still reads as "unreported" — the external contract is unchanged — but a schema mismatch during a rolling upgrade is no longer silent.

Verification

  • cargo test --workspace, cargo clippy --workspace --all-targets clean
  • Java: full :test suite, :javadoc, spotlessCheck
  • Node: 469 tests, biome + tsc
  • Python: dashboard settings suite, ruff + mypy
  • Docs site builds

Summary by CodeRabbit

  • New Features
    • Java handlers can signal retryable vs permanent failures via dedicated exceptions that override retryOn, including when wrapped in cause chains.
    • All SDKs now expose reserved dashboard settings-key prefixes so the generic settings surface can hide them consistently.
  • Bug Fixes
    • Dashboard settings reliably hide and refuse reserved keys (including retention:effective:default) across supported SDKs.
    • Unparseable published retention data is now safely unreported but emits a warning for visibility.
  • Documentation
    • Updated reliability and retry guidance to reflect typed exception precedence and wrapped-cause behavior.

A handler throwing RetryableException or NonRetryableException overrides the task retryOn predicate.
Every shell now derives its hidden-key list from the core instead of hardcoding one, so a missed prefix cannot reopen the hole in one binding.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fe6f513f-2572-40da-be26-72bc46f8e9ab

📥 Commits

Reviewing files that changed from the base of the PR and between 43c5843 and 7ad8ef1.

📒 Files selected for processing (4)
  • sdks/java/src/main/java/org/byteveda/taskito/dashboard/api/SettingsHandlers.java
  • sdks/java/src/main/java/org/byteveda/taskito/dashboard/store/SettingsAccess.java
  • sdks/java/src/test/java/org/byteveda/taskito/dashboard/InMemorySettings.java
  • sdks/java/src/test/java/org/byteveda/taskito/dashboard/ReservedSettingPrefixesTest.java

📝 Walkthrough

Walkthrough

The PR centralizes reserved settings prefixes across Rust, Java, Node, and Python dashboard APIs, adds retention parse warnings, and introduces Java typed retry exceptions with centralized worker classification, documentation, and tests.

Changes

Cross-SDK reserved settings

Layer / File(s) Summary
Reserved settings contract and core source
crates/taskito-core/BINDING_CONTRACT.md, crates/taskito-core/src/*
The core defines and exports reserved settings prefixes, documents shell API behavior, and warns when published retention data is invalid.
Binding prefix exports
crates/taskito-java/src/queue/admin.rs, crates/taskito-node/src/lib.rs, crates/taskito-python/src/lib.rs, sdks/java/..., sdks/python/taskito/_taskito.pyi
Java JNI, Node, and Python bindings expose the core prefix list.
Dashboard settings enforcement
sdks/java/.../SettingsHandlers*, sdks/node/src/dashboard/..., sdks/node/test/..., sdks/python/taskito/dashboard/..., sdks/python/tests/...
Dashboard settings handlers derive protected prefixes dynamically and test retention keys as hidden and non-editable.

Java typed retry signals

Layer / File(s) Summary
Typed exception contract and documentation
sdks/java/src/main/java/org/byteveda/taskito/errors/*, sdks/java/src/main/java/org/byteveda/taskito/task/Task.java, docs/content/docs/java/*, docs/content/docs/shared/guides/reliability/*
Adds RetryableException and NonRetryableException and documents their precedence, cause-chain handling, and SDK-specific retry behavior.
Retry classification and worker integration
sdks/java/src/main/java/org/byteveda/taskito/worker/*
Centralizes typed-signal and predicate classification in RetryDecision and uses it during job failure handling.
Retry behavior validation
sdks/java/src/test/java/org/byteveda/taskito/{core,worker}/*
Tests hierarchy, precedence, wrapped and cyclic causes, predicate failures, retry exhaustion, and immediate dead-lettering.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TaskHandler
  participant WorkerDispatchBridge
  participant RetryDecision
  participant JobBackend
  TaskHandler->>WorkerDispatchBridge: throw typed or ordinary Throwable
  WorkerDispatchBridge->>RetryDecision: isRetryable(retryOn, error)
  RetryDecision->>RetryDecision: inspect cause chain or evaluate retryOn
  RetryDecision-->>WorkerDispatchBridge: retryability result
  WorkerDispatchBridge->>JobBackend: failJob(error, retryability)
Loading

Possibly related PRs

Suggested labels: dashboard

Suggested reviewers: pratyush618

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds cross-SDK reserved-settings prefix plumbing and retention/dashboard docs, which are unrelated to issue #484. Split the reserved-prefix, retention, and dashboard KV changes into a separate PR so this one stays focused on typed Java retry exceptions.
Docstring Coverage ⚠️ Warning Docstring coverage is 57.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main Java change: signaling retry intent through exception type.
Linked Issues check ✅ Passed The typed retry exceptions, worker classification, and Java docs all match issue #484’s requirements.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/java-typed-retry-exceptions

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.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@sdks/java/src/main/java/org/byteveda/taskito/dashboard/api/SettingsHandlers.java`:
- Around line 9-23: Remove eager NativeQueue.reservedSettingPrefixes()
evaluation from the static PROTECTED_PREFIXES initializer in SettingsHandlers.
Resolve the protected prefixes lazily or inject them so loading
SettingsHandlers, including InMemorySettings tests, does not trigger
NativeLoader; preserve consistent filtering once the prefixes are requested.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e0c9d6e-507f-433c-b1a0-c68121239ac6

📥 Commits

Reviewing files that changed from the base of the PR and between 950cefd and 43c5843.

📒 Files selected for processing (29)
  • crates/taskito-core/BINDING_CONTRACT.md
  • crates/taskito-core/src/lib.rs
  • crates/taskito-core/src/scheduler/retention.rs
  • crates/taskito-core/src/settings.rs
  • crates/taskito-java/src/queue/admin.rs
  • crates/taskito-node/src/lib.rs
  • crates/taskito-python/src/lib.rs
  • docs/content/docs/java/api-reference/errors.mdx
  • docs/content/docs/java/api-reference/task.mdx
  • docs/content/docs/shared/guides/reliability/error-handling.mdx
  • docs/content/docs/shared/guides/reliability/retries.mdx
  • sdks/java/src/main/java/org/byteveda/taskito/dashboard/api/SettingsHandlers.java
  • sdks/java/src/main/java/org/byteveda/taskito/errors/NonRetryableException.java
  • sdks/java/src/main/java/org/byteveda/taskito/errors/RetryableException.java
  • sdks/java/src/main/java/org/byteveda/taskito/errors/package-info.java
  • sdks/java/src/main/java/org/byteveda/taskito/internal/NativeQueue.java
  • sdks/java/src/main/java/org/byteveda/taskito/task/Task.java
  • sdks/java/src/main/java/org/byteveda/taskito/worker/RetryDecision.java
  • sdks/java/src/main/java/org/byteveda/taskito/worker/WorkerDispatchBridge.java
  • sdks/java/src/test/java/org/byteveda/taskito/core/ExceptionHierarchyTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/dashboard/api/SettingsHandlersTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/worker/RetryDecisionTest.java
  • sdks/java/src/test/java/org/byteveda/taskito/worker/TypedRetryExceptionTest.java
  • sdks/node/src/dashboard/handlers/settings.ts
  • sdks/node/src/native.ts
  • sdks/node/test/dashboard/opsSettings.test.ts
  • sdks/python/taskito/_taskito.pyi
  • sdks/python/taskito/dashboard/handlers/settings.py
  • sdks/python/tests/dashboard/test_dashboard_settings.py

Comment thread sdks/java/src/main/java/org/byteveda/taskito/dashboard/api/SettingsHandlers.java Outdated
Keeps dashboard class loading off the native library; the in-memory test store mirrors the core list, locked by a test.
@pratyush618
pratyush618 merged commit 9cbac9e into master Jul 23, 2026
34 checks passed
@pratyush618
pratyush618 deleted the feat/java-typed-retry-exceptions branch July 23, 2026 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add typed Retryable and NonRetryable exceptions to the Java SDK

2 participants