refactor(java): typed exception hierarchy#334
Conversation
Add SerializationException (+ CryptoException), WorkflowException, LockException, ConfigurationException, WebhookException under errors/, all extending TaskitoException, and route the SDK's throw sites to them so callers can catch a category or the base type.
Subtype relationships, and that serializers throw Serialization/Crypto.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughSix new exception types are added under ChangesTyped Exception Hierarchy
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
sdks/java/src/main/java/org/byteveda/taskito/serialization/EncryptedSerializer.java (1)
31-43: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winPreserve delegate
SerializationExceptions here.
delegate.serialize(value)is inside the cryptotry, so a plain payload-serialization failure now gets wrapped asCryptoException. That collapses the distinction this PR is introducing: callers usingEncryptedSerializercan no longer tell “couldn’t serialize the payload” from “encryption failed.”Suggested fix
`@Override` public byte[] serialize(Object value) { + byte[] plaintext = delegate.serialize(value); try { byte[] iv = new byte[IV_LENGTH]; random.nextBytes(iv); Cipher cipher = Cipher.getInstance(TRANSFORMATION); cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_BITS, iv)); - byte[] ciphertext = cipher.doFinal(delegate.serialize(value)); + byte[] ciphertext = cipher.doFinal(plaintext); byte[] out = new byte[IV_LENGTH + ciphertext.length]; System.arraycopy(iv, 0, out, 0, IV_LENGTH); System.arraycopy(ciphertext, 0, out, IV_LENGTH, ciphertext.length); return out; } catch (Exception e) { throw new CryptoException("encryption failed", e); } }🤖 Prompt for 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. In `@sdks/java/src/main/java/org/byteveda/taskito/serialization/EncryptedSerializer.java` around lines 31 - 43, The `EncryptedSerializer.serialize` method is catching `delegate.serialize(value)` failures together with encryption errors, which wraps plain payload serialization problems as `CryptoException`. Split the handling in `serialize` so the `delegate.serialize(value)` call is allowed to surface its `SerializationException` unchanged, and only the cipher/IV/encryption steps are wrapped in `CryptoException`; keep the change localized to `EncryptedSerializer` and preserve the existing crypto error path.
🧹 Nitpick comments (1)
sdks/java/src/test/java/org/byteveda/taskito/ExceptionHierarchyTest.java (1)
31-47: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the other remapped throw sites in this cohort.
This test file proves
JsonSerializerandSignedSerializer, but the PR also changesEncryptedSerializerandJobStatus.fromWire(...). A regression in either public contract would still pass this suite.Possible additions
+import org.byteveda.taskito.model.JobStatus; +import org.byteveda.taskito.serialization.EncryptedSerializer;`@Test` void tamperedSignedPayloadThrowsCryptoException() { Serializer signed = new SignedSerializer(new JsonSerializer(), "secret".getBytes()); byte[] bytes = signed.serialize(42); bytes[0] ^= 0x01; // corrupt the HMAC tag @@ assertInstanceOf(SerializationException.class, error); assertInstanceOf(TaskitoException.class, error); } + + `@Test` + void tamperedEncryptedPayloadThrowsCryptoException() { + Serializer encrypted = new EncryptedSerializer(new JsonSerializer(), "0123456789abcdef".getBytes()); + byte[] bytes = encrypted.serialize(42); + bytes[bytes.length - 1] ^= 0x01; + + assertThrows(CryptoException.class, () -> encrypted.deserialize(bytes, Integer.class)); + } + + `@Test` + void unknownJobStatusThrowsSerializationException() { + assertThrows(SerializationException.class, () -> JobStatus.fromWire("bogus")); + }🤖 Prompt for 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. In `@sdks/java/src/test/java/org/byteveda/taskito/ExceptionHierarchyTest.java` around lines 31 - 47, Add tests in ExceptionHierarchyTest to cover the other remapped throw sites in this cohort: verify EncryptedSerializer.deserialize throws the expected exception type on bad or tampered input, and verify JobStatus.fromWire(...) maps invalid wire values to the correct exception category. Use the existing patterns in malformedPayloadThrowsSerializationException() and tamperedSignedPayloadThrowsCryptoException() to keep the assertions aligned with the public contracts.
🤖 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/DefaultTaskito.java`:
- Around line 382-383: The nested workflow validation path in DefaultTaskito is
still throwing TaskitoException from encodeChild(), which leaves
submitWorkflow(...) with mixed exception types for the same validation failure.
Update encodeChild() and any related child-step validation in DefaultTaskito to
throw WorkflowException consistently, matching the existing submit-time check
for missing payloads. Keep the exception messages descriptive for the invalid
child step so callers can catch WorkflowException for all workflow validation
failures.
---
Outside diff comments:
In
`@sdks/java/src/main/java/org/byteveda/taskito/serialization/EncryptedSerializer.java`:
- Around line 31-43: The `EncryptedSerializer.serialize` method is catching
`delegate.serialize(value)` failures together with encryption errors, which
wraps plain payload serialization problems as `CryptoException`. Split the
handling in `serialize` so the `delegate.serialize(value)` call is allowed to
surface its `SerializationException` unchanged, and only the
cipher/IV/encryption steps are wrapped in `CryptoException`; keep the change
localized to `EncryptedSerializer` and preserve the existing crypto error path.
---
Nitpick comments:
In `@sdks/java/src/test/java/org/byteveda/taskito/ExceptionHierarchyTest.java`:
- Around line 31-47: Add tests in ExceptionHierarchyTest to cover the other
remapped throw sites in this cohort: verify EncryptedSerializer.deserialize
throws the expected exception type on bad or tampered input, and verify
JobStatus.fromWire(...) maps invalid wire values to the correct exception
category. Use the existing patterns in
malformedPayloadThrowsSerializationException() and
tamperedSignedPayloadThrowsCryptoException() to keep the assertions aligned with
the public contracts.
🪄 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: 20bcce17-9bc9-4767-9ae9-1681db8da9a0
📒 Files selected for processing (23)
sdks/java/src/main/java/org/byteveda/taskito/DefaultTaskito.javasdks/java/src/main/java/org/byteveda/taskito/Taskito.javasdks/java/src/main/java/org/byteveda/taskito/dashboard/DashboardServer.javasdks/java/src/main/java/org/byteveda/taskito/errors/ConfigurationException.javasdks/java/src/main/java/org/byteveda/taskito/errors/CryptoException.javasdks/java/src/main/java/org/byteveda/taskito/errors/LockException.javasdks/java/src/main/java/org/byteveda/taskito/errors/SerializationException.javasdks/java/src/main/java/org/byteveda/taskito/errors/WebhookException.javasdks/java/src/main/java/org/byteveda/taskito/errors/WorkflowException.javasdks/java/src/main/java/org/byteveda/taskito/errors/package-info.javasdks/java/src/main/java/org/byteveda/taskito/events/EventName.javasdks/java/src/main/java/org/byteveda/taskito/locks/Lock.javasdks/java/src/main/java/org/byteveda/taskito/model/JobStatus.javasdks/java/src/main/java/org/byteveda/taskito/serialization/EncryptedSerializer.javasdks/java/src/main/java/org/byteveda/taskito/serialization/JsonSerializer.javasdks/java/src/main/java/org/byteveda/taskito/serialization/SignedSerializer.javasdks/java/src/main/java/org/byteveda/taskito/webhooks/Deliverer.javasdks/java/src/main/java/org/byteveda/taskito/webhooks/WebhookManager.javasdks/java/src/main/java/org/byteveda/taskito/webhooks/WebhookStore.javasdks/java/src/main/java/org/byteveda/taskito/worker/Worker.javasdks/java/src/main/java/org/byteveda/taskito/workflows/WorkflowRun.javasdks/java/src/main/java/org/byteveda/taskito/workflows/WorkflowTracker.javasdks/java/src/test/java/org/byteveda/taskito/ExceptionHierarchyTest.java
Replaces blanket
TaskitoExceptionthrows with a small typed hierarchy socallers can catch the failure class that matters to them. Every new type
extends the root
TaskitoException, so existingcatch (TaskitoException)keeps working — non-breaking.
New
errors/packageSerializationException(+ subclassCryptoExceptionfor sign/encrypt failures)WorkflowException— workflow submit/track/compensation failuresLockException,ConfigurationException,WebhookExceptionRewiring
~30 throw sites across serializers, locks, webhooks, workflows, dashboard, and
the producer/admin surface now throw the specific type. Native JNI boundary
errors stay base
TaskitoException(their cause is opaque).Tests
ExceptionHierarchyTestasserts each type is catchable asTaskitoExceptionand that the documented throw sites raise the specific subclass.
Prerequisite for the saga PR (compensation failures throw
WorkflowException).Rebased onto master after #333.
Summary by CodeRabbit
WorkflowExceptionfor certain payloadless structural steps and missing required workflow step data.ConfigurationException,SerializationException,CryptoException,LockException,WebhookException,WorkflowException) to support finer-grained exception handling.