• Projects
  • Service
  • About
  • branding.bz
  • Podcast
  • Tips
  • FAQ
  • Recruit
  • Download
  • Contact
  • branding.bz(ブランド構築SaaS)
  • DESIGN NOW(デザインメディア)
  • X
  • LinkedIn
  • Spotify
  • Facebook

213-0011 神奈川県川崎市高津区久本3-6-7-303

© 2026 ID INC. All rights reserved

claude-skills/スキル
SKILLOfficialdevelopment

output-error-http-client

プラグイン
outputai
ソース
GitHub で見る ↗
説明

HTTP クライアント(通信プログラム)の不正な使い方を Output SDK ステップ内で修正します。 次のような場合に使用: - 追跡されていないリクエスト(通信要求)がある - エラーの詳しい内容が記録されていない - axios(通信ライブラリ)に関するエラーが出ている - HTTP 通信がログに記録されず、再試行されていない

原文を表示

Fix HTTP client misuse in Output SDK steps. Use when seeing untraced requests, missing error details, axios-related errors, or when HTTP calls aren't being properly logged and retried.

ユースケース
  • 追跡されていないリクエストを修正する
  • エラー内容の記録がないときに対応
  • axiosエラーを修正する
  • HTTP通信のログ記録と再試行を設定
本文(日本語訳)

HTTP クライアント誤用を修正する

概要

このスキルは、Output SDK の @outputai/http から提供される createKyClient を使わずに、axios や fetch などの HTTP クライアントを直接使用する場合に生じる問題を診断して修正します。Output SDK のクライアントを使うことで、リクエストの追跡、自動再試行、より良いエラー処理が実現できます。

次のような場合に使用:

  • HTTP リクエストがワークフローの追跡ログに記録されていない
  • 失敗したリクエストのエラー情報が不足している
  • axios 関連のエラーやインポートの問題が生じている
  • HTTP の失敗時に再試行が機能していない
  • タイムアウト動作が一貫していない

根本原因

axios、fetch、その他の HTTP クライアントを直接使うと、Output SDK の以下の機能がバイパスされてしまいます:

  • リクエスト・レスポンス追跡: ワークフロー追跡にログが記録されない
  • 自動再試行: 失敗したリクエストが再試行されない
  • エラー標準化: エラー形式に一貫性がない
  • タイムアウト処理: ステップのタイムアウトと統合されない

典型的な誤った使い方

axios を直接使用

// 誤り: 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;
  }
} );

fetch を直接使用

// 誤り: fetch を使用
export const fetchData = step( {
  name: 'fetchData',
  fn: async input => {
    const response = await fetch( 'https://api.example.com/data' );
    return response.json();
  }
} );

解決方法

@outputai/http の createKyClient を使用してください:

基本的な使い方

import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/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/http';

const client = createKyClient( {
  prefix: 'https://api.example.com',
  timeout: 30000,  // 30秒のタイムアウト
  retry: {
    limit: 3,      // 最大3回再試行
    methods: [ 'GET', 'POST' ],  // 再試行対象のHTTPメソッド
    statusCodes: [ 408, 500, 502, 503, 504 ]  // 再試行をトリガーするステータスコード
  },
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }
} );

HTTP メソッド

GET リクエスト

const data = await client.get( 'users/123' ).json();

POST リクエスト

const result = await client.post( 'users', {
  json: {
    name: 'John',
    email: 'john@example.com'
  }
} ).json();

PUT リクエスト

const updated = await client.put( 'users/123', {
  json: {
    name: 'John Updated'
  }
} ).json();

DELETE リクエスト

await client.delete( 'users/123' );

クエリパラメータ付き

const data = await client.get( 'search', {
  searchParams: {
    q: 'query',
    limit: 10
  }
} ).json();

メタデータのみの取得

レスポンスのメタデータ(response.url、response.status、ヘッダーなど)だけを読む場合は、使用されないボディをキャンセルしてください。.json() や .text() などでボディを読むと、既に消費されます。

const response = await client.get( url );

try {
  return response.url;
} finally {
  await response.body?.cancel();
}

移行例

変更前(誤り - axios を使用)

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;
    }
  }
} );

変更後(正しい - createKyClient を使用)

import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/http';
import { credentials } from '@outputai/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;
  }
} );

エラーハンドリング

Ky クライアントは構造化されたエラーハンドリングを提供します:

import { createKyClient, ky } from '@outputai/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 ) {
        // レスポンスの詳細情報にアクセス可能
        const status = error.response.status;
        const body = await error.response.json();
        throw new Error( `API returned ${status}: ${body.message}` );
      }
      throw error;
    }
  }
} );

axios および fetch の使用箇所を探す

コードベース内で検索してください:

# axios のインポートを検索
grep -rn "from 'axios'\|from \"axios\"" src/

# fetch の呼び出しを検索
grep -rn "await fetch(" src/

# その他の HTTP ライブラリを検索
grep -rn "got\|node-fetch\|request\|superagent" src/

createKyClient のメリット

  1. 追跡: リクエストがワークフロー追跡に処理時間とともに記録される
  2. 自動再試行: 一時的な失敗に対する再試行ロジックを設定可能
  3. 一貫したエラー: すべてのリクエストでエラー形式が標準化される
  4. タイムアウト統合: ステップおよびワークフローのタイムアウトと連携する
  5. 型安全性: TypeScript の完全サポート

設定オプション

オプション 説明 デフォルト
prefix すべてのリクエストのベース URL (必須)
timeout リクエストのタイムアウト(ミリ秒) 10000
retry.limit 最大再試行回数 2
retry.methods 再試行対象の HTTP メソッド ['GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE']
retry.statusCodes 再試行をトリガーするステータスコード [408, 413, 429, 500, 502, 503, 504]
headers デフォルトヘッダー {}

動作確認

createKyClient への移行後:

  1. ワークフロー実行: npx output workflow run <name> --input '<input>'
  2. 追跡を確認: npx output workflow debug <id> --json
  3. 追跡の検証: HTTP リクエストがステップ追跡に表示されること
  4. 再試行をテスト: 失敗をシミュレートして再試行動作を確認

関連するスキル

  • ワークフロー関数での I/O については、output-error-direct-io を参照
  • 接続の問題については、output-services-check を参照
  • 暗号化されたシークレット管理については、output-dev-credentials を参照
原文(English)を表示

Fix HTTP Client Misuse

Overview

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/http. The Output SDK client provides tracing, automatic retries, and better error handling.

When to Use This Skill

You're seeing:

  • Untraced HTTP requests (not appearing in workflow traces)
  • Missing error details for failed requests
  • axios-related errors or import issues
  • Retries not working for HTTP failures
  • Inconsistent timeout behavior

Root Cause

Using axios, fetch, or other HTTP clients directly bypasses Output SDK's:

  • Request/response tracing: Calls aren't logged in workflow traces
  • Automatic retries: Failed requests aren't retried
  • Error standardization: Error formats may be inconsistent
  • Timeout handling: Timeouts may not integrate with step timeouts

Symptoms

Using axios Directly

// 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;
  }
} );

Using fetch Directly

// WRONG: Using fetch
export const fetchData = step( {
  name: 'fetchData',
  fn: async input => {
    const response = await fetch( 'https://api.example.com/data' );
    return response.json();
  }
} );

Solution

Use createKyClient from @outputai/http:

Basic Usage

import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/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 };
  }
} );

With Full Configuration

import { createKyClient } from '@outputai/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'
  }
} );

HTTP Methods

GET Request

const data = await client.get( 'users/123' ).json();

POST Request

const result = await client.post( 'users', {
  json: {
    name: 'John',
    email: 'john@example.com'
  }
} ).json();

PUT Request

const updated = await client.put( 'users/123', {
  json: {
    name: 'John Updated'
  }
} ).json();

DELETE Request

await client.delete( 'users/123' );

With Query Parameters

const data = await client.get( 'search', {
  searchParams: {
    q: 'query',
    limit: 10
  }
} ).json();

Metadata-Only Responses

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();
}

Complete Migration Example

Before (Wrong - using axios)

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;
    }
  }
} );

After (Correct - using createKyClient)

import { z, step } from '@outputai/core';
import { createKyClient } from '@outputai/http';
import { credentials } from '@outputai/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;
  }
} );

Error Handling

The Ky client provides structured error handling:

import { createKyClient, ky } from '@outputai/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 new Error( `API returned ${status}: ${body.message}` );
      }
      throw error;
    }
  }
} );

Finding axios/fetch Usage

Search your codebase:

# Find axios imports
grep -rn "from 'axios'\|from \"axios\"" src/

# Find fetch calls
grep -rn "await fetch(" src/

# Find other HTTP libraries
grep -rn "got\|node-fetch\|request\|superagent" src/

Benefits of createKyClient

  1. Tracing: Requests appear in workflow traces with timing
  2. Automatic Retries: Configurable retry logic for transient failures
  3. Consistent Errors: Standardized error format across all requests
  4. Timeout Integration: Works with step and workflow timeouts
  5. Type Safety: Full TypeScript support

Configuration Options

Option Description Default
prefix Base URL for all requests (required)
timeout Request timeout in ms 10000
retry.limit Max retry attempts 2
retry.methods HTTP methods to retry ['GET', 'PUT', 'HEAD', 'DELETE', 'OPTIONS', 'TRACE']
retry.statusCodes Status codes to retry [408, 413, 429, 500, 502, 503, 504]
headers Default headers {}

Verification

After migrating to createKyClient:

  1. Run the workflow: npx output workflow run <name> --input '<input>'
  2. Check the trace: npx output workflow debug <id> --json
  3. Verify tracing: HTTP requests should appear in the step trace
  4. Test retries: Simulate failures to verify retry behavior

Related Issues

  • For I/O in workflow functions, see output-error-direct-io
  • For connection issues, see output-services-check
  • For encrypted secrets management, see output-dev-credentials

原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。