Skip to content

[Bug]: Stop or execution timeout during an active step leaves terminal status missing from traces #848

Open

Description

@zhongkechen

Summary

When a durable execution is stopped or reaches its execution timeout while a step is still running, the invocation can survive long enough to reach its invocation-end hook. The next checkpoint is rejected, but the SDK/plugin contract reports only a retry disposition. The resulting trace never records the execution's actual STOPPED or TIMED_OUT outcome.

This spans the core SDK, plugin lifecycle interface, and OTel consumers. The existing error and hook payloads do not provide enough information for a status-mapping-only fix.

Expected Behavior

When an invocation survives the external termination and reaches finalization, the SDK/plugin integration should be able to discover and export the authoritative terminal execution status. Both Execution and Invocation views should make that outcome visible; the Execution view should export the terminal Workflow span with durable.execution.status=STOPPED or TIMED_OUT.

Execution outcome and invocation disposition should remain distinct. The terminal execution must not be represented solely by RETRYING with no final execution status.

Actual Behavior

The supplied cloud report describes this sequence:

  1. A step runs for approximately 60 seconds.
  2. The execution is stopped approximately 20 seconds into the running step, or reaches an ExecutionTimeout of 30 seconds.
  3. The current Lambda invocation continues and the step runs for the full 60 seconds.
  4. Its next checkpoint fails with InvalidParameterValueException: Invalid checkpoint token.
  5. The plugin ends Invocation with OTel status UNSET and durable.invocation.status=RETRYING.
  6. No further invocation follows because the durable execution has already ended.
  7. No span carries durable.execution.status; the Execution view never exports Workflow.

Continuing the current invocation after StopDurableExecution is documented platform behavior. This issue concerns the missing terminal telemetry, not a requirement to forcibly interrupt running user code.

Steps to Reproduce

  1. Deploy a handler with a named durable step whose body takes 60 seconds to complete. A sleep/delay inside that step is a test fixture, not a durable-wait implementation.
  2. Set the Lambda function/invocation timeout above 60 seconds (for example, 120 seconds), and enable an OTel plugin with sampling and export enabled.
  3. Run each trace view separately.
  4. For the stop case, configure an execution timeout above the test duration, start an execution, and call StopDurableExecution approximately 20 seconds after the step starts.
  5. For the timeout case, configure the durable execution timeout to 30 seconds, start an execution, and do not issue a stop.
  6. Wait for the running invocation to finish, inspect service execution status/history, and inspect exported spans correlated by execution ARN.
  7. Compare the service's STOPPED/TIMED_OUT outcome with the final Invocation attributes and missing Workflow.

SDK Version

Reported main revision: 64339d4f (Java).

Java Version

Not specified in the supplied report.

Is this a regression?

Unknown; no last-known-good version was established.

Source Analysis

The core classifies this checkpoint rejection as retryable, and the executor consequently reports InvocationStatus.RETRYING to the end hook. Both OTel views intentionally leave RETRYING as UNSET because the hook cannot distinguish a stopped/timed-out execution from an invocation failure that may be retried. Workflow is materialized only for SUCCEEDED or FAILED.

An invalid checkpoint token does not uniquely identify STOPPED or TIMED_OUT: it can also mean a stale/superseded invocation while the execution remains active. The surviving invocation provides an opportunity to retrieve the real outcome, not proof that the current error already contains it.

Implementation Direction and Acceptance Criteria

  • Obtain authoritative terminal execution information through an appropriate service signal or a bounded lookup such as GetDurableExecution, and expose it through the SDK/plugin lifecycle contract independently of invocation retry disposition.
  • In both trace views, export the confirmed STOPPED/TIMED_OUT execution status; export the terminal Workflow once with the authoritative execution end time when available.
  • Preserve existing behavior for genuine retryable invocation failures and stale-token rejection while the execution is still active. Do not infer terminal status solely from the invalid-token message.
  • Define and test the fallback when status retrieval fails or is unavailable: preserve uncertainty without fabricating a terminal outcome or blocking invocation finalization indefinitely.
  • Add focused core/plugin tests and deployed regression coverage for active-step stop and active-step execution timeout. Compare service status/history with exported spans and cover both trace views.
  • Add a nonterminal invalid-token control case and a successful retry/resume case, preserving replay behavior and ensuring completed work is not repeated.
  • Document the limitation for termination while no invocation is running or after runtime shutdown; an in-process finalization lookup alone cannot cover those cases. Coordinate any shared conformance requirement changes separately.

Additional Context

Reported environment: AWS Lambda in us-west-2; traces inspected in Dash0. The original report covers JavaScript, Python, and Java, in both Execution and Invocation views.

The cloud observations above come from the supplied report. The linked implementation was reviewed at the reported revision; no additional cloud reproduction or Dash0 retrieval was performed for filing this issue.

Related but different: #705 fixed invalid-token message matching. Preserve the corrected retryable classification for stale tokens belonging to an execution that is still active. #706 handles checkpoint responses with a missing token; this report concerns a rejected checkpoint after external stop or execution timeout.

Cross-SDK Tracking

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