Skip to content
Development
Skill

/output-error-nondeterminism

Fix non-determinism errors in Output SDK workflows. Use when seeing replay failures, inconsistent results between runs, "non-deterministic" error messages, or workflows behaving differently on retry.

From plugin
output
44451 skills5 agents

Claude Code

$ npx -y skills add growthxai/output --skill output-error-nondeterminism --agent claude-code

SKILL.md

output-error-nondeterminism.SKILL.md
name: output-error-nondeterminism
description: Fix non-determinism errors in Output SDK workflows. Use when seeing replay failures, inconsistent results between runs, "non-deterministic" error messages, or workflows behaving differently on retry.
allowed-tools: [Bash, Read]

Fix Non-Determinism Errors

Overview

This skill helps diagnose and fix non-determinism errors in Output SDK workflows. Workflows must be deterministic because Temporal may replay them during recovery or retries, and the replay must produce identical results.

When to Use This Skill

You're seeing:

  • "non-deterministic" error messages
  • Replay failures after workflow restart
  • Inconsistent results between runs with same input
  • Errors during workflow recovery
  • Warnings about determinism violations

Root Cause

Temporal workflows must be deterministic: given the same input, they must always execute the same sequence of operations. This is because Temporal replays workflow history to recover state after crashes or restarts.

Non-deterministic operations break this replay mechanism because they produce different values each time.

Common Causes and Solutions

1. Math.random()

**Problem**: Random values differ on each execution.

// WRONG: Non-deterministic
export default workflow( {
  fn: async input => {
    const id = Math.random().toString( 36 );  // Different each time!
    return await processWithId( { id } );
  }
} );

**Solution**: Pass random values as workflow input or generate in a step.

// Option 1: Pass as input
export default workflow( {
  inputSchema: z.object( {
    id: z.string()  // Generate ID before calling workflow
  } ),
  fn: async input => {
    return await processWithId( { id: input.id } );
  }
} );

// Option 2: Generate in a step (steps can be non-deterministic)
export const generateId = step( {
  name: 'generateId',
  fn: async () => ( { id: Math.random().toString( 36 ) } )
} );

export default workflow( {
  fn: async input => {
    const { id } = await generateId( {} );
    return await processWithId( { id } );
  }
} );

2. Date.now() / new Date()

**Problem**: Timestamps change between executions.

// WRONG: Non-deterministic
export default workflow( {
  fn: async input => {
    const timestamp = Date.now();  // Different each replay!
    return await logEvent( { timestamp } );
  }
} );

**Solution**: Pass timestamps as input or use Temporal's time API.

// Option 1: Pass as input
export default workflow( {
  inputSchema: z.object( {
    timestamp: z.number()
  } ),
  fn: async input => {
    return await logEvent( { timestamp: input.timestamp } );
  }
} );

// Option 2: Generate in a step
export const getTimestamp = step( {
  name: 'getTimestamp',
  fn: async () => ( { timestamp: Date.now() } )
} );

3. crypto.randomUUID()

**Problem**: UUIDs differ each execution.

// WRONG: Non-deterministic
import { randomUUID } from 'crypto';

export default workflow( {
  fn: async input => {
    const requestId = randomUUID();  // Different each time!
    return await makeRequest( { requestId } );
  }
} );

**Solution**: Generate UUIDs as input or in steps.

// Correct: Generate in step
export const generateRequestId = step( {
  name: 'generateRequestId',
  fn: async () => {
    const { randomUUID } = await import( 'crypto' );
    return { requestId: randomUUID() };
  }
} );

4. Dynamic Imports

**Problem**: Dynamic imports may resolve differently.

// WRONG: Non-deterministic import timing
export default workflow( {
  fn: async input => {
    const module = await import( `./handlers/${input.type}` );
    return module.handle( input );
  }
} );

**Solution**: Use static imports and conditional logic.

// Correct: Static imports with conditional use
import { handleTypeA } from './handlers/typeA';
import { handleTypeB } from './handlers/typeB';

export default workflow( {
  fn: async input => {
    if ( input.type === 'A' ) {
      return await handleTypeA( input );
    } else {
      return await handleTypeB( input );
    }
  }
} );

5. Environment Variables

**Problem**: Environment may differ between replays.

// WRONG: Environment can change
export default workflow( {
  fn: async input => {
    const apiUrl = process.env.API_URL;  // May differ on different workers
    return await callApi( { url: apiUrl } );
  }
} );

**Solution**: Pass configuration as input or use constants.

// Correct: Pass as input
export default workflow( {
  inputSchema: z.object( {
    apiUrl: z.string()
  } ),
  fn: async input => {
    return await callApi( { url: input.apiUrl } );
  }
} );

How to Find Non-Deterministic Code

Search for Common Patterns

# Find Math.random usage
grep -rn "Math.random" src/workflows/

# Find Date.now or new Date
grep -rn "Date.now\|new Date" src/workflows/

# Find crypto random functions
grep -rn "randomUUID\|randomBytes" src/workflows/

# Find dynamic imports
grep -rn "import(" src/workflows/

Review Workflow Files

Look at your workflow `fn` functions specifically. Non-deterministic code is only a problem **in workflow functions**, not in step functions.

Verification Steps

1. **Fix the code** using solutions above 2. **Run the workflow**: `npx output workflow run <name> --input '<input>'` 3. **Run again with same input**: Result should be identical 4. **Check for errors**: No "non-deterministic" messages

The Determinism Rule

**Workflow functions must be deterministic:**

  • Same input = same execution path
  • No side effects (network, filesystem, random values)
  • Only orchestration logic and step calls

**Step functions can be non-deterministic:**

  • Steps record their results in Temporal history
  • Replays use recorded results, not re-execution
  • All I/O should happen in steps

Debugging Tip

If unsure whether code is causing issues:

Read more
@growthxai
Ships withoutput

The open-source TypeScript framework for building AI workflows and agents. Designed for Claude Code — describe what you want, Claude builds it, with all the best practices already in place. One framework.

44451 skills5 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
444
Star history of growthxai/output on GitHub, a chart by star-history.com

Other skills on output.

output-build-workflow

output-build-workflow

Implement an Output SDK workflow from a plan document. Use when the user asks to build, implement, or code a workflow from an…

@growthxai@growthxaiInstall Skill
output-credentials-edit

output-credentials-edi…

View, edit, and set encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying…

@growthxai@growthxaiInstall Skill
output-credentials-init

output-credentials-ini…

Initialize encrypted credentials for an Output.ai project. Use when setting up credentials for the first time, adding…

@growthxai@growthxaiInstall Skill
output-debug-workflow

output-debug-workflow

Debug Output SDK workflow issues. Use when user reports a workflow failing, erroring, hanging, producing wrong results, or asks…

@growthxai@growthxaiInstall Skill
output-dev-agent-class

output-dev-agent-class

Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when…

@growthxai@growthxaiInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about output-error-nondeterminism.

Which coding agents can use output-error-nondeterminism?
output-error-nondeterminism installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.