Skip to content

bug: getRunIdForOptions function is duplicated, contains inconsistent fallback behavior #2852

@robraux

Description

@robraux

Provide environment information

System:
OS: macOS 15.7.3
CPU: (16) arm64 Apple M4 Max
Memory: 1.99 GB / 64.00 GB
Shell: 5.9 - /bin/zsh

Binaries:
Node: 22.16.0 -
Yarn: 1.22.22
npm: 10.9.2
pnpm: 10.26.2
Watchman: 2026.01.05.00

Describe the bug

The getRunIdForOptions function is duplicated in two locations with inconsistent fallback behavior. The SDK version (packages/trigger-sdk/src/v3/streams.ts) lacks fallback logic that exists in the core implementation (packages/core/src/v3/realtimeStreams/manager.ts).

When calling streams.pipe("key", stream, { target: "root" }) from a root task (a task not triggered by another task), the SDK throws an error instead of falling back to the current run ID. The semantic meaning of target: "root" is "send to the root of this task tree" - if the current task IS the root, it should behave the same as target: "self".

Reproduction repo

This can be reproduced in any Trigger.dev project using the SDK's streams.pipe() API with target: "root" from a directly-triggered task.

To reproduce

import { streams, task } from "@trigger.dev/sdk";

  // This task is triggered directly (not a child of another task)
  export const rootTask = task({
    id: "root-task",
    run: async (payload) => {
      const myStream = createSomeAsyncGenerator();

      // This will throw an error even though it should work
      const { waitUntilComplete } = streams.pipe("output", myStream, {
        target: "root"  // Fails because rootTaskRunId is undefined for root tasks
      });

      await waitUntilComplete();
    },
  });

Steps:

  1. Create a task that is triggered directly (not as a child of another task)
  2. Call streams.pipe() with { target: "root" }
  3. Observe the error is thrown

Additional information

Root Cause: Two getRunIdForOptions functions exist with different behavior:

File Fallback for "root"/"parent"?
packages/trigger-sdk/src/v3/streams.ts:665-683 ❌ No
packages/core/src/v3/realtimeStreams/manager.ts:141-159 ✅ Yes

Why the core fallback is unreachable: The SDK resolves targets to string IDs FIRST, then passes them to the core. By the time StandardRealtimeStreamsManager.pipe() is called, it receives a string ID (or an error was already thrown), so its fallback logic is dead code for SDK callers.

Evidence the fallback is intended:

  • Documentation (docs/tasks/streams.mdx:374,399) shows target: "root" as valid usage
  • Internal API (packages/core/src/v3/runMetadata/manager.ts:330-333) passes semantic targets directly and relies on the fallback

Origin: Both functions introduced in same commit (536d9fa21 - "feat(realtime): Realtime streams v2 #2632"). Inconsistency was present from creation.

Affected functions: streams.pipe(), streams.append(), streams.writer() - all use the same getRunIdForOptions in streams.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions