Skip to content

[copilot-finds] Bug: getOrchestrationHistory() does not destroy gRPC stream, risking resource leak and unhandled error crash #215

@github-actions

Description

@github-actions

Problem

getOrchestrationHistory() in packages/durabletask-js/src/client/client.ts (line ~955) calls stream.removeAllListeners() when the gRPC server stream ends or errors, but does not call stream.destroy() or register a no-op error handler afterward.

This creates two risks:

  1. Resource leak: The underlying gRPC HTTP/2 stream is never explicitly released. Node.js may eventually garbage-collect it, but during high-throughput scenarios (e.g., polling many orchestration histories), leaked streams can exhaust file descriptors or memory.

  2. Unhandled error crash: After removeAllListeners(), if the gRPC transport emits a late "error" event (e.g., connection reset during teardown), there is no handler registered. Node.js treats unhandled "error" events as fatal, crashing the process.

Root Cause

The cleanup pattern in the "end" and "error" handlers of getOrchestrationHistory() is incomplete. It removes listeners but does not destroy the stream or guard against late errors.

Correct Pattern (Already Used in Worker)

task-hub-grpc-worker.ts (lines 388-395, 415-417) uses the correct three-step cleanup:

stream.removeAllListeners();
stream.on("error", () => {});  // Guard against late errors
stream.destroy();              // Release resources

This pattern was missed in the client's streaming method.

Proposed Fix

Add stream.on("error", () => {}) and stream.destroy() after stream.removeAllListeners() in both the "end" and "error" handlers of getOrchestrationHistory(), matching the worker's established pattern.

Impact

  • Severity: Medium — resource leak under normal operation; potential process crash under network instability
  • Affected scenarios: Any code calling client.getOrchestrationHistory(), including the export-history package and user applications that inspect orchestration history
  • Likelihood: Low for the crash (requires specific timing of late gRPC errors), moderate for the resource leak (every call leaks until GC)

Metadata

Metadata

Assignees

No one assigned

    Labels

    copilot-findsFindings from daily automated code review agent

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions