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…
Fix HTTP client misuse in Output SDK steps. Use when seeing untraced requests, missing error details, axios-related errors, HTTP calls aren't being properly logged and retried, or paid API costs are missing from cost reports.
Claude Code
$ npx -y skills add growthxai/output --skill output-error-http-client --agent claude-code
Codex
$ npx skills add growthxai/output --skill output-error-http-client -a codex -y
Cursor
$ npx skills add growthxai/output --skill output-error-http-client -a cursor -y
Gemini CLI
$ npx skills add growthxai/output --skill output-error-http-client -a gemini-cli -y
GitHub Copilot
$ npx skills add growthxai/output --skill output-error-http-client -a github-copilot -y
OpenCode
$ npx skills add growthxai/output --skill output-error-http-client -a opencode -y
Windsurf
$ npx skills add growthxai/output --skill output-error-http-client -a windsurf -y
name: output-error-http-client description: Fix HTTP client misuse in Output SDK steps. Use when seeing untraced requests, missing error details, axios-related errors, HTTP calls aren't being properly logged and retried, or paid API costs are missing from cost reports. allowed-tools: [Bash, Read]
This skill helps diagnose and fix issues caused by using axios, fetch, or other HTTP clients directly instead of Output SDK's `createKyClient` from `@outputai/core/http`. The Output SDK client provides tracing, automatic retries, and better error handling.
You're seeing:
Using axios, fetch, or other HTTP clients directly bypasses Output SDK's:
// WRONG: Using axios
import axios from 'axios';
export const fetchData = step( {
name: 'fetchData',
fn: async input => {
const response = await axios.get( 'https://api.example.com/data' );
return response.data;
}
} );// WRONG: Using fetch
export const fetchData = step( {
name: 'fetchData',
fn: async input => {
const response = await fetch( 'https://api.example.com/data' );
return response.json();
}
} );Use `createKyClient` from `@outputai/core/http`:
import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/core/http';
export const fetchData = step( {
name: 'fetchData',
inputSchema: z.object( {
endpoint: z.string()
} ),
outputSchema: z.object( {
data: z.unknown()
} ),
fn: async input => {
const client = createKyClient( {
prefix: 'https://api.example.com'
} );
const data = await client.get( input.endpoint ).json();
return { data };
}
} );import { createKyClient } from '@outputai/core/http';
const client = createKyClient( {
prefix: 'https://api.example.com',
timeout: 30000, // 30 second timeout
retry: {
limit: 3, // Retry up to 3 times
methods: [ 'GET', 'POST' ], // Which methods to retry
statusCodes: [ 408, 500, 502, 503, 504 ] // Which status codes trigger retry
},
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
} );const data = await client.get( 'users/123' ).json();
const result = await client.post( 'users', {
json: {
name: 'John',
email: 'john@example.com'
}
} ).json();const updated = await client.put( 'users/123', {
json: {
name: 'John Updated'
}
} ).json();await client.delete( 'users/123' );
const data = await client.get( 'search', {
searchParams: {
q: 'query',
limit: 10
}
} ).json();When code only reads metadata from a non-`HEAD` response, such as `response.url`, `response.status`, or headers, cancel the unused body. Reading a body with `.json()`, `.text()`, etc. already consumes it.
const response = await client.get( url );
try {
return response.url;
} finally {
await response.body?.cancel();
}import axios from 'axios';
import { step } from '@outputai/core';
export const createUser = step( {
name: 'createUser',
fn: async input => {
try {
const response = await axios.post(
'https://api.example.com/users',
{ name: input.name, email: input.email },
{
headers: { 'Authorization': `Bearer ${process.env.API_KEY}` },
timeout: 30000
}
);
return response.data;
} catch ( error ) {
if ( axios.isAxiosError( error ) ) {
throw new Error( `API Error: ${error.response?.data?.message}` );
}
throw error;
}
}
} );import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/core/http';
import { credentials } from '@outputai/core/credentials';
export const createUser = step( {
name: 'createUser',
inputSchema: z.object( {
name: z.string(),
email: z.string().email()
} ),
outputSchema: z.object( {
id: z.string(),
name: z.string(),
email: z.string()
} ),
fn: async input => {
const client = createKyClient( {
prefix: 'https://api.example.com',
timeout: 30000,
retry: { limit: 3 },
headers: {
'Authorization': `Bearer ${credentials.require( 'service.api_key' )}`
}
} );
const user = await client.post( 'users', {
json: {
name: input.name,
email: input.email
}
} ).json();
return user;
}
} );The Ky client provides structured error handling:
import { createKyClient, ky } from '@outputai/core/http';
export const fetchData = step( {
name: 'fetchData',
fn: async input => {
const client = createKyClient( { prefix: 'https://api.example.com' } );
try {
return await client.get( 'data' ).json();
} catch ( error ) {
if ( error instanceof ky.HTTPError ) {
// Access response details
const status = error.response.status;
const body = await error.response.json();
throw nThe 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.
Implement an Output SDK workflow from a plan document. Use when the user asks to build, implement, or code a workflow from an…
View, edit, and set encrypted credentials in an Output.ai project. Use when adding secrets, updating API keys, verifying…
Wire encrypted credentials to environment variables using the credential: convention. Use when setting up LLM provider keys…
Initialize encrypted credentials for an Output.ai project. Use when setting up credentials for the first time, adding…
Debug Output SDK workflow issues. Use when user reports a workflow failing, erroring, hanging, producing wrong results, or asks…
Use the Agent class for multi-step tool loops, conversation history, streaming progress, and reusable LLM agents. Use when…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.