/output-error-missing-schemas
Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed.
$ npx -y skills add growthxai/output --skill output-error-missing-schemas --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/output-error-missing-schemas
Context preview
The summary Claude sees to decide when to auto-load this skill.
Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed.
SKILL.md
output-error-missing-schemas.SKILL.mdname: output-error-missing-schemas
description: Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed.
allowed-tools: [Bash, Read]
Fix Missing Schema Definitions
Overview
This skill helps diagnose and fix issues caused by steps that lack explicit `inputSchema` or `outputSchema` definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
When to Use This Skill
You're seeing:
- Type errors at step boundaries
- Undefined properties in step inputs/outputs
- Validation failures when passing data between steps
- TypeScript errors about incompatible types
- Runtime errors about unexpected data shapes
Root Cause
Steps without explicit schemas:
- Don't validate input data at runtime
- Don't provide TypeScript type inference
- May serialize/deserialize data incorrectly
- Can pass undefined or malformed data silently
Symptoms
Missing Input Schema
// WRONG: No input validation
export const processData = step( {
name: 'processData',
// inputSchema: missing!
outputSchema: z.object( { result: z.string() } ),
fn: async input => {
return { result: input.value }; // input.value might be undefined!
}
} );Missing Output Schema
// WRONG: No output validation
export const fetchData = step( {
name: 'fetchData',
inputSchema: z.object( { id: z.string() } ),
// outputSchema: missing!
fn: async input => {
return { data: await getFromApi( input.id ) }; // Output shape not validated
}
} );Both Schemas Missing
// WRONG: No validation at all
export const transformData = step( {
name: 'transformData',
// No schemas!
fn: async input => {
return transform( input );
}
} );Solution
Always define both `inputSchema` and `outputSchema` for every step:
Complete Step Definition
import { z, step } from '@outputai/core';
export const processData = step( {
name: 'processData',
inputSchema: z.object( {
id: z.string(),
value: z.number(),
optional: z.string().optional()
} ),
outputSchema: z.object( {
result: z.string(),
processedAt: z.number()
} ),
fn: async input => {
// input is fully typed: { id: string, value: number, optional?: string }
return {
result: `Processed ${input.id}`,
processedAt: Date.now()
};
// output is validated against outputSchema
}
} );Schema Definition Best Practices
Use Descriptive Schemas
// Good: Clear, descriptive schema
inputSchema: z.object( {
userId: z.string().uuid(),
email: z.string().email(),
age: z.number().int().positive()
} )Handle Optional Fields
inputSchema: z.object( {
required: z.string(),
optional: z.string().optional(),
withDefault: z.string().default( 'fallback' )
} )Use Schema Composition
// Define reusable schemas
const userSchema = z.object( {
id: z.string(),
name: z.string()
} );
const addressSchema = z.object( {
street: z.string(),
city: z.string()
} );
// Compose in step
inputSchema: z.object( {
user: userSchema,
address: addressSchema
} )Handle Arrays and Nested Objects
inputSchema: z.object( {
items: z.array( z.object( {
id: z.string(),
quantity: z.number()
} ) ),
metadata: z.record( z.string() )
} )Finding Steps Without Schemas
Search your codebase:
# Find step definitions
grep -rn "step({" src/workflows/
# Look for steps without inputSchema
grep -A5 "step({" src/workflows/ | grep -B2 "fn:"
# Check if schemas are present
grep -rn "inputSchema:" src/workflows/
grep -rn "outputSchema:" src/workflows/Review each step definition to ensure both schemas are present.
Benefits of Explicit Schemas
1. **Runtime Validation**: Catches data errors early 2. **Type Safety**: Full TypeScript inference in step functions 3. **Documentation**: Schemas document expected data shapes 4. **Serialization**: Ensures proper data serialization between steps 5. **Error Messages**: Clear validation errors when data is wrong
Common Schema Patterns
API Response Steps
export const fetchUser = step( {
name: 'fetchUser',
inputSchema: z.object( {
userId: z.string()
} ),
outputSchema: z.object( {
user: z.object( {
id: z.string(),
name: z.string(),
email: z.string()
} ).nullable(), // Handle not found
found: z.boolean()
} ),
fn: async input => {
const user = await api.getUser( input.userId );
return { user, found: user !== null };
}
} );Transformation Steps
export const transformData = step( {
name: 'transformData',
inputSchema: z.object( {
raw: z.array( z.unknown() )
} ),
outputSchema: z.object( {
processed: z.array( z.object( {
id: z.string(),
value: z.number()
} ) ),
count: z.number()
} ),
fn: async input => {
const processed = input.raw.map( transformItem );
return { processed, count: processed.length };
}
} );Void Output Steps
For steps that don't return meaningful data:
export const logEvent = step( {
name: 'logEvent',
inputSchema: z.object( {
event: z.string(),
data: z.record( z.unknown() )
} ),
outputSchema: z.object( {
logged: z.literal( true )
} ),
fn: async input => {
await logger.log( input.event, input.data );
return { logged: true };
}
} );Verification
After adding schemas:
1. **TypeScript check**: `npm run output:worker:build` should pass without type errors 2. **Runtime test**: `npx output workflow run <name> --input '<input>'` should validate correctly 3. **Invalid input test**: Pass invalid data and verify validation errors appear
Related Issues
- For Zod import issues, see `outp
Read more
name: output-error-missing-schemas description: Fix missing schema definitions in Output SDK steps. Use when seeing type errors, undefined properties at step boundaries, validation failures, or when step inputs/outputs aren't being properly typed. allowed-tools: [Bash, Read]
Fix Missing Schema Definitions
Overview
This skill helps diagnose and fix issues caused by steps that lack explicit `inputSchema` or `outputSchema` definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
When to Use This Skill
You're seeing:
- Type errors at step boundaries
- Undefined properties in step inputs/outputs
- Validation failures when passing data between steps
- TypeScript errors about incompatible types
- Runtime errors about unexpected data shapes
Root Cause
Steps without explicit schemas:
- Don't validate input data at runtime
- Don't provide TypeScript type inference
- May serialize/deserialize data incorrectly
- Can pass undefined or malformed data silently
Symptoms
Missing Input Schema
// WRONG: No input validation
export const processData = step( {
name: 'processData',
// inputSchema: missing!
outputSchema: z.object( { result: z.string() } ),
fn: async input => {
return { result: input.value }; // input.value might be undefined!
}
} );Missing Output Schema
// WRONG: No output validation
export const fetchData = step( {
name: 'fetchData',
inputSchema: z.object( { id: z.string() } ),
// outputSchema: missing!
fn: async input => {
return { data: await getFromApi( input.id ) }; // Output shape not validated
}
} );Both Schemas Missing
// WRONG: No validation at all
export const transformData = step( {
name: 'transformData',
// No schemas!
fn: async input => {
return transform( input );
}
} );Solution
Always define both `inputSchema` and `outputSchema` for every step:
Complete Step Definition
import { z, step } from '@outputai/core';
export const processData = step( {
name: 'processData',
inputSchema: z.object( {
id: z.string(),
value: z.number(),
optional: z.string().optional()
} ),
outputSchema: z.object( {
result: z.string(),
processedAt: z.number()
} ),
fn: async input => {
// input is fully typed: { id: string, value: number, optional?: string }
return {
result: `Processed ${input.id}`,
processedAt: Date.now()
};
// output is validated against outputSchema
}
} );Schema Definition Best Practices
Use Descriptive Schemas
// Good: Clear, descriptive schema
inputSchema: z.object( {
userId: z.string().uuid(),
email: z.string().email(),
age: z.number().int().positive()
} )Handle Optional Fields
inputSchema: z.object( {
required: z.string(),
optional: z.string().optional(),
withDefault: z.string().default( 'fallback' )
} )Use Schema Composition
// Define reusable schemas
const userSchema = z.object( {
id: z.string(),
name: z.string()
} );
const addressSchema = z.object( {
street: z.string(),
city: z.string()
} );
// Compose in step
inputSchema: z.object( {
user: userSchema,
address: addressSchema
} )Handle Arrays and Nested Objects
inputSchema: z.object( {
items: z.array( z.object( {
id: z.string(),
quantity: z.number()
} ) ),
metadata: z.record( z.string() )
} )Finding Steps Without Schemas
Search your codebase:
# Find step definitions
grep -rn "step({" src/workflows/
# Look for steps without inputSchema
grep -A5 "step({" src/workflows/ | grep -B2 "fn:"
# Check if schemas are present
grep -rn "inputSchema:" src/workflows/
grep -rn "outputSchema:" src/workflows/Review each step definition to ensure both schemas are present.
Benefits of Explicit Schemas
1. **Runtime Validation**: Catches data errors early 2. **Type Safety**: Full TypeScript inference in step functions 3. **Documentation**: Schemas document expected data shapes 4. **Serialization**: Ensures proper data serialization between steps 5. **Error Messages**: Clear validation errors when data is wrong
Common Schema Patterns
API Response Steps
export const fetchUser = step( {
name: 'fetchUser',
inputSchema: z.object( {
userId: z.string()
} ),
outputSchema: z.object( {
user: z.object( {
id: z.string(),
name: z.string(),
email: z.string()
} ).nullable(), // Handle not found
found: z.boolean()
} ),
fn: async input => {
const user = await api.getUser( input.userId );
return { user, found: user !== null };
}
} );Transformation Steps
export const transformData = step( {
name: 'transformData',
inputSchema: z.object( {
raw: z.array( z.unknown() )
} ),
outputSchema: z.object( {
processed: z.array( z.object( {
id: z.string(),
value: z.number()
} ) ),
count: z.number()
} ),
fn: async input => {
const processed = input.raw.map( transformItem );
return { processed, count: processed.length };
}
} );Void Output Steps
For steps that don't return meaningful data:
export const logEvent = step( {
name: 'logEvent',
inputSchema: z.object( {
event: z.string(),
data: z.record( z.unknown() )
} ),
outputSchema: z.object( {
logged: z.literal( true )
} ),
fn: async input => {
await logger.log( input.event, input.data );
return { logged: true };
}
} );Verification
After adding schemas:
1. **TypeScript check**: `npm run output:worker:build` should pass without type errors 2. **Runtime test**: `npx output workflow run <name> --input '<input>'` should validate correctly 3. **Invalid input test**: Pass invalid data and verify validation errors appear
Related Issues
- For Zod import issues, see `outp
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.
Repo: growthxai/output
Other skills on output.
- /llm-output-schema-constraints
Zod schema constraints that Anthropic rejects or silently ignores when sent as structured-output tool definitions via Output.object(). Use when writing or reviewing Zod schemas passed to Output.object(), or debugging structured-output validation errors.
Open skill - /prompt-file-provider-options
Guide to the providerOptions structure in .prompt files — decision tree for where an option goes, common mistakes, per-provider quick reference, and Anthropic prompt caching. Use when writing or reviewing .prompt file frontmatter (provider, model, providerOptions,
Open skill - /validate
Run lint, build, and tests to validate changes are correct
Open skill - /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 existing plan, or after output-plan-workflow has produced a plan and the user is ready to build.
Open skill - /output-credentials-edit
View and edit encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying credential values, or retrieving a specific credential.
Open skill - /output-credentials-env-vars
Wire encrypted credentials to environment variables using the credential: convention. Use when setting up LLM provider keys (ANTHROPIC_API_KEY, OPENAI_API_KEY) or any env var that should come from encrypted credentials.
Open skill

