Skip to content

[Bug]: Type-only callback errors append ': null' to caller and telemetry messages #849

Open

Description

@zhongkechen

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: null

For the full SDK-to-telemetry path:

  1. Register DurableExecutionTelemetry.durableExecutionTelemetry(...) with a capturing exporter and error details enabled.
  2. Run a named wait-for-callback and complete its callback with failure carrying only ErrorType.
  3. Allow the callback failure to escape the handler. Inspect the top-level exported error.message, and any containing operation error that was checkpointed.
  4. 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

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-triageIssue needs triagepkg:pluginsPlugin interface, possibly impacting all pluginspkg:sdkModule: sdk

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions