• 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通信ライブラリ)に関連するエラーが発生している - HTTP通信が適切にログ記録・再試行されていない - 有料 API の利用コストが料金レポートに反映されていない

原文を表示

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.

ユースケース
  • トレースされていないリクエストが見られる
  • エラーの詳細情報が不足しているとき
  • axiosに関連するエラーが発生している
  • HTTP通信がログ記録・再試行されていない
  • APIコストが料金レポートに反映されていない
本文(日本語訳)

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

メタデータのみの応答

非HEADリクエストの応答からメタデータ(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;
    }
  }
} );

有料APIのコスト追跡漏れ

症状

クライアントが有料の外部APIをラップしているが、npx output workflow costのAPI Costsセクションに呼び出しが表示されない。トレースは機能しているものの、コストは常に$0または報告書から欠落している。

根本原因

npx output workflow costは、@outputai/httpのaddRequestCostで明示的に付加された(関連付けられた)HTTPコストのみを認識します。リクエストのトレースは自動ですが、コストの計上は自動ではありません。addRequestCostを呼び出さないクライアントはトレースが正しく機能してコストが0になりますが、これは無料/内部サービスでは期待通りですが、有料サービスではバグです。

解決方法

各呼び出しサイトではなく、クライアントのafterResponseフック(処理後コールバック)内にコストを付加して、すべての呼び出しが自動的にコスト計上されるようにします。メーター化されたレスポンスおよび定額料金のパターンについてはoutput-dev-http-client-create(「リクエストコストの付加(有料API)」セクション)を、外部観測システムにコストを転送する方法についてはoutput-dev-cost-hooksを参照してください。

// 間違い: 有料API、コスト追跡なし
const client = createKyClient( { prefix: 'https://api.service.com' } );

// 正しい: すべての呼び出しに対してコストが自動的に付加される
import { addRequestCost, createKyClient } from '@outputai/http';

const client = createKyClient( {
  prefix: 'https://api.service.com',
  hooks: {
    afterResponse: [
      ( _request, _options, response ) => {
        if ( !response.ok ) return;
        addRequestCost( response, 0.005 );
      }
    ]
  }
} );

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を参照
  • リクエストコストの付加および転送については、output-dev-http-client-createおよびoutput-dev-cost-hooksを参照
原文(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;
    }
  }
} );

Missing Cost Tracking for Paid APIs

Symptom

A client wraps a paid third-party API, but its calls never show up in the API Costs section of npx output workflow cost — they're traced (so tracing works), but their cost is always $0 or absent from the report.

Root Cause

npx output workflow cost only knows about HTTP spend that was explicitly attached with addRequestCost from @outputai/http. Tracing a request is automatic; costing it is not — a client that never calls addRequestCost will trace correctly and cost nothing, which is expected for free/internal services but a bug for paid ones.

Solution

Attach cost inside an afterResponse hook on the client so every call is costed automatically, rather than at each call site. See output-dev-http-client-create (section "Attaching Request Cost (Paid APIs)") for the metered-response and flat-rate patterns, and output-dev-cost-hooks for forwarding those costs to an external observability system.

// WRONG: paid API, no cost tracking
const client = createKyClient( { prefix: 'https://api.service.com' } );

// CORRECT: cost attached automatically for every call
import { addRequestCost, createKyClient } from '@outputai/http';

const client = createKyClient( {
  prefix: 'https://api.service.com',
  hooks: {
    afterResponse: [
      ( _request, _options, response ) => {
        if ( !response.ok ) return;
        addRequestCost( response, 0.005 );
      }
    ]
  }
} );

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
  • For attaching and forwarding request cost, see output-dev-http-client-create and output-dev-cost-hooks

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