Repository navigation
[Bug]: Type-only callback errors append ': null' to caller and telemetry messages #849
Description
Expected Behavior
A callback failed with only ErrorType: "RejectedError" must not produce the literal text "null" as if it were an error message. The caller-facing exception should have a useful type-only display message, and telemetry records should not publish RejectedError: null.
Preserve the original ErrorObject, including the absence of ErrorMessage, and retain the existing callback exception class and retry/replay behavior.
Actual Behavior
CallbackFailedException.buildMessage concatenates errorType + ": " + errorMessage whenever the type is present, including when the message is null. Consequently, a type-only external failure produces:
RejectedError: null
The core-generated message also reaches Durable Execution Telemetry. Its nameAndMessage helper reads the checkpointed ErrorObject when available, but falls back to Throwable.getMessage() when ErrorMessage is absent. An uncaught CallbackFailedException therefore produces a telemetry error whose name can correctly be RejectedError while its message is still RejectedError: null. A containing operation that checkpoints that caller error can carry the malformed message into later snapshots as well.
This affects error records sent to the configured telemetry destinations; it is not limited to OTel exception events. The callback leaf's original error type can already be preserved by the telemetry exporter, so do not conflate this message-formatting defect with #847.
Steps to Reproduce
The core formatting behavior has this minimal reproduction using public types:
var operation = Operation.builder()
.id("callback")
.type(OperationType.CALLBACK)
.status(OperationStatus.FAILED)
.callbackDetails(CallbackDetails.builder()
.error(ErrorObject.builder()
.errorType("RejectedError")
.build())
.build())
.build();
var failure = new CallbackFailedException(operation);
System.out.println(failure.getMessage()); // RejectedError: nullFor the full SDK-to-telemetry path:
- Register
DurableExecutionTelemetry.durableExecutionTelemetry(...)with a capturing exporter and error details enabled. - Run a named wait-for-callback and complete its callback with failure carrying only ErrorType.
- Allow the callback failure to escape the handler. Inspect the top-level exported
error.message, and any containing operation error that was checkpointed. - Also test a caught failure followed by a durable wait and resume, checking the stored operation error and exported record without repeating completed user work.
SDK Version
Source inspected at Java commit 4b1aa82b0d898fea495cda45760ce3737fbb341e. Exact deployed core/exporter JAR versions for the supplied observation were not recorded.
Java Version
Not recorded for the supplied cloud observation. The source defect is ordinary null String concatenation.
Is this a regression?
Unknown; no last-known-good version was established.
Source Evidence
- CallbackFailedException.buildMessage
- Telemetry name/message conversion and fallback
- Execution error included in telemetry records
Acceptance Criteria
- Type-only callback failures do not append
: null; message-only and type-plus-message failures preserve their supplied text. - Cover absent Error, empty ErrorObject, absent/empty type, and absent/empty message without introducing a new exception or changing failure classification.
- Preserve the underlying stored ErrorObject and its absent fields; define the caller's type-only display message explicitly.
- Add focused coverage through the real SDK and telemetry plugin for uncaught and caught/resumed failures. Check root and applicable operation error records, including the name-keyed format when a single operation represents that name.
- Preserve replayed results, callback identity and side-effect counts.
- Do not broaden this fix into the separate OTel operation exception-type issue [Bug]: OTel operation spans report DurableOperationException instead of the original error type #847.
Source inspection confirms the formatting and telemetry fallback paths. No new cloud deployment or raw backend retrieval was performed for filing this issue. Related #773 fixed the absent-Error NullPointerException; it does not cover the type-only : null message.
Activity