Skip to content

SDK query()の改善:forループのbreakやusing宣言でサブプロセスを自動クリーンアップ ​

原文(日本語に翻訳) ​

for await からの break や await using の使用時にサブプロセスと一時ファイルをクリーンアップするよう SDK query() を改善

原文(英語) ​

Improved SDK query() to clean up subprocess and temp files when consumers break from for await or use await using

概要 ​

Claude Agent SDKの query() 関数は非同期イテレータを返しますが、for await ループを途中で break した場合や await using 構文でリソースを解放した場合に、内部で起動するサブプロセスや一時ファイルが適切にクリーンアップされない問題が修正されました。長時間動作するアプリケーションやCLIツールでリソースリークを防ぐために重要な改善です。

基本的な使い方 ​

for await ループでのbreak(修正後) ​

typescript
import { query } from "@anthropic-ai/claude-code";

// breakしても内部リソースが自動クリーンアップされる
for await (const message of query({ prompt: "大きなタスクを実行して" })) {
  if (message.type === "result" && message.subtype === "success") {
    console.log("完了:", message.result);
    break; // 修正後: サブプロセスと一時ファイルが自動クリーンアップ
  }
}

await using 構文(TypeScript 5.2+) ​

typescript
import { query } from "@anthropic-ai/claude-code";

async function runTask() {
  // await using によって自動的にクリーンアップが保証される
  await using session = query({ prompt: "タスクを実行して" });
  
  for await (const message of session) {
    console.log(message);
    // スコープを抜けると自動的にクリーンアップ
  }
}

実践例 ​

条件付き早期終了のパターン ​

typescript
import { query } from "@anthropic-ai/claude-code";

async function findFirst(searchTerm: string): Promise<string | null> {
  for await (const message of query({
    prompt: `"${searchTerm}" を含む最初のファイルを教えて`
  })) {
    if (message.type === "result" && message.subtype === "success") {
      return message.result; // breakせずreturnでも正しくクリーンアップ
    }
    
    // エラーの場合は早期終了
    if (message.type === "result" && message.subtype === "error") {
      break; // 修正後: リソースが正しく解放される
    }
  }
  
  return null;
}

タイムアウト付きの実行 ​

typescript
import { query } from "@anthropic-ai/claude-code";

async function queryWithTimeout(prompt: string, timeoutMs: number) {
  const timeoutPromise = new Promise<never>((_, reject) => {
    setTimeout(() => reject(new Error("タイムアウト")), timeoutMs);
  });
  
  try {
    for await (const message of query({ prompt })) {
      // タイムアウトが発生してもサブプロセスはクリーンアップされる
      if (message.type === "result") {
        return message;
      }
    }
  } catch (error) {
    // エラー時もリソースは適切に解放される
    console.error("エラー:", error);
    throw error;
  }
}

長時間動作するサーバーアプリケーション ​

typescript
import { query } from "@anthropic-ai/claude-code";
import express from "express";

const app = express();

app.post("/analyze", async (req, res) => {
  try {
    // リクエストごとにquery()を呼び出す
    // 修正前: 各リクエスト後にサブプロセスが残留し、サーバーがリソース枯渇
    // 修正後: 各リクエスト完了後に自動クリーンアップ
    for await (const message of query({
      prompt: req.body.code,
      options: { /* ... */ }
    })) {
      if (message.type === "result" && message.subtype === "success") {
        res.json({ result: message.result });
        return; // returnでもクリーンアップが保証される
      }
    }
  } catch (error) {
    res.status(500).json({ error: String(error) });
  }
});

await using を使った確実なクリーンアップ(TypeScript 5.2+) ​

typescript
import { query } from "@anthropic-ai/claude-code";

// tsconfig.json で "target": "ES2022" 以上が必要
async function processWithGuaranteedCleanup() {
  await using iterator = query({ prompt: "データを処理して" });
  
  for await (const event of iterator) {
    if (event.type === "text") {
      process.stdout.write(event.text);
    }
    
    if (event.type === "result") {
      break; // スコープを抜けると await using が自動クリーンアップ
    }
  }
  // ここでクリーンアップが実行される
}

注意点 ​

  • TypeScript/JavaScript SDK向け: この修正はClaude Code SDK(@anthropic-ai/claude-code)に関するものです
  • await using の要件: await using 構文はTypeScript 5.2以上と "target": "ES2022" 以上が必要です
  • 以前の回避策: 以前は try/finally ブロックで手動クリーンアップが必要でしたが、修正後は不要です
  • 長寿命プロセスへの影響: Webサーバーや長時間動作するCLIツールでSDKを使用している場合、この修正によるリソースリークの解消が特に重要です

関連情報 ​