Skip to main content

Overview

getRun() retrieves a Run object for an existing workflow execution, allowing you to check status, read results, access streams, and control execution.

Usage

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');
const status = await run.status;

Signature

function getRun<TResult>(runId: string): Run<TResult>
runId
string
required
The workflow run ID (format: wrun_{ulid})
Returns: Run<TResult> - A Run instance for interacting with the workflow

Run Class

The Run class provides methods and properties for workflow interaction:

Properties

runId
string
The unique identifier for this workflow run
status
Promise<WorkflowRunStatus>
Current status: 'pending', 'running', 'completed', 'failed', or 'cancelled'
returnValue
Promise<TResult>
The workflow’s return value. Polls until completion.Throws:
  • WorkflowRunFailedError if the workflow failed
  • WorkflowRunCancelledError if the workflow was cancelled
workflowName
Promise<string>
The name of the workflow function
createdAt
Promise<Date>
Timestamp when the workflow run was created
startedAt
Promise<Date | undefined>
Timestamp when execution started, or undefined if not started yet
completedAt
Promise<Date | undefined>
Timestamp when execution completed, or undefined if not completed yet
readable
ReadableStream
The default readable stream for this workflow. Reads chunks written via getWritable().

Methods

getReadable()

Get a readable stream for this workflow run.
run.getReadable<R>(options?: WorkflowReadableStreamOptions): ReadableStream<R>
options
WorkflowReadableStreamOptions

wakeUp()

Interrupt pending sleep() calls. See Run.wakeUp().
run.wakeUp(options?: StopSleepOptions): Promise<StopSleepResult>

cancel()

Cancel the workflow execution.
run.cancel(): Promise<void>

Examples

Check Status

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');
const status = await run.status;

switch (status) {
  case 'pending':
    console.log('Workflow queued but not started');
    break;
  case 'running':
    console.log('Workflow executing');
    break;
  case 'completed':
    console.log('Workflow finished successfully');
    break;
  case 'failed':
    console.log('Workflow encountered an error');
    break;
  case 'cancelled':
    console.log('Workflow was cancelled');
    break;
}

Get Return Value

import { getRun } from 'workflow/runtime';
import { 
  WorkflowRunFailedError,
  WorkflowRunCancelledError 
} from '@workflow/errors';

const run = getRun('wrun_123');

try {
  const result = await run.returnValue;
  console.log('Workflow result:', result);
} catch (error) {
  if (WorkflowRunFailedError.is(error)) {
    console.error('Workflow failed:', error.message);
    console.error('Run ID:', error.runId);
  } else if (WorkflowRunCancelledError.is(error)) {
    console.log('Workflow was cancelled');
  }
}

Stream Results

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');
const reader = run.readable.getReader();

try {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    console.log('Received:', value);
  }
} finally {
  reader.releaseLock();
}

Multiple Streams

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');

// Read from different stream namespaces
const progressStream = run.getReadable({ namespace: 'progress' });
const logsStream = run.getReadable({ namespace: 'logs' });

// Process streams independently
const progressReader = progressStream.getReader();
const logsReader = logsStream.getReader();

Cancel Workflow

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');

// Cancel the workflow
await run.cancel();

const status = await run.status;
console.log(status); // 'cancelled'

Workflow Metadata

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');

const [name, created, started, completed] = await Promise.all([
  run.workflowName,
  run.createdAt,
  run.startedAt,
  run.completedAt,
]);

console.log('Workflow:', name);
console.log('Created:', created);
console.log('Started:', started);
console.log('Completed:', completed);

if (started && completed) {
  const duration = completed.getTime() - started.getTime();
  console.log('Duration:', duration, 'ms');
}

Resume from Stream Position

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');

// Start reading from chunk 100
const stream = run.getReadable({ startIndex: 100 });
const reader = stream.getReader();

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  
  processChunk(value);
}

API Route Handler

// app/api/workflow/[runId]/route.ts
import { getRun } from 'workflow/runtime';

export async function GET(
  request: Request,
  { params }: { params: { runId: string } }
) {
  const run = getRun(params.runId);
  
  const [status, name, createdAt] = await Promise.all([
    run.status,
    run.workflowName,
    run.createdAt,
  ]);
  
  return Response.json({
    runId: run.runId,
    status,
    workflowName: name,
    createdAt,
  });
}

Streaming Response

// app/api/workflow/[runId]/stream/route.ts
import { getRun } from 'workflow/runtime';

export async function GET(
  request: Request,
  { params }: { params: { runId: string } }
) {
  const run = getRun(params.runId);
  
  // Pipe workflow stream to response
  return new Response(run.readable, {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  });
}

Poll for Completion

import { getRun } from 'workflow/runtime';

const run = getRun('wrun_123');

// Poll every second until completed
while (true) {
  const status = await run.status;
  
  if (status === 'completed' || status === 'failed' || status === 'cancelled') {
    break;
  }
  
  await new Promise(resolve => setTimeout(resolve, 1000));
}

console.log('Workflow finished');

Error Handling

WorkflowRunFailedError

import { getRun } from 'workflow/runtime';
import { WorkflowRunFailedError } from '@workflow/errors';

const run = getRun('wrun_123');

try {
  const result = await run.returnValue;
} catch (error) {
  if (WorkflowRunFailedError.is(error)) {
    console.error('Workflow failed');
    console.error('Run ID:', error.runId);
    console.error('Error:', error.workflowError?.message);
    console.error('Stack:', error.workflowError?.stack);
  }
}

WorkflowRunCancelledError

import { getRun } from 'workflow/runtime';
import { WorkflowRunCancelledError } from '@workflow/errors';

const run = getRun('wrun_123');

try {
  const result = await run.returnValue;
} catch (error) {
  if (WorkflowRunCancelledError.is(error)) {
    console.log('Workflow was cancelled');
    console.log('Run ID:', error.runId);
  }
}

WorkflowRunNotFoundError

import { getRun } from 'workflow/runtime';
import { WorkflowRunNotFoundError } from '@workflow/errors';

const run = getRun('wrun_invalid');

try {
  const status = await run.status;
} catch (error) {
  if (WorkflowRunNotFoundError.is(error)) {
    console.error('Run not found:', error.runId);
  }
}

Type Safety

import { getRun } from 'workflow/runtime';

// Define workflow return type
interface WorkflowResult {
  success: boolean;
  data: string[];
}

// Type-safe run instance
const run = getRun<WorkflowResult>('wrun_123');

// Return value is typed
const result = await run.returnValue;
console.log(result.success); // ✓ TypeScript knows the shape
console.log(result.data);    // ✓ Typed as string[]

Best Practices

  1. Type the result: Use getRun<TResult>() for type-safe return values
  2. Handle all error cases: Check for failed, cancelled, and not-found errors
  3. Use streams for real-time updates: Don’t poll status, use streams instead
  4. Store run IDs: Persist run IDs in your database for later retrieval
  5. Check status before actions: Verify workflow state before cancel/wakeUp operations
  6. Clean up readers: Always release stream readers when done

See Also

Build docs developers (and LLMs) love