ポートフォリオサイトのロゴ
Icon for Cloudflare Workers環境で「Illegal invocation」エラーが起きたときに試したこと

Cloudflare Workers環境で「Illegal invocation」エラーが起きたときに試したこと

Cloudflare Workers上でMCPサーバーを構築し、Notion APIを使用する際に遭遇した「TypeError: Illegal invocation」エラーの根本原因の特定から解決までの検証プロセスを詳しく解説します。

この記事について

Cloudflare Workers 上で MCP サーバーを構築し、Notion API を使用してデータベース検索機能を実装する際に「TypeError: Illegal invocation」エラーに遭遇しました。

この記事では、エラーの根本原因の特定から解決までの検証プロセスを、実際の体験をもとに詳しく解説します。

想定読者

  • Cloudflare Workers での開発経験がある
  • Notion API または外部 API クライアントライブラリを使用している
  • 「Illegal invocation」エラーに遭遇している

この記事で得られること

  • Cloudflare Workers 環境特有の this コンテキスト問題の理解
  • fetch.bind(globalThis) による根本的な解決方法

システム概要と目標

Cloudflare Workers 上で MCP(Model Context Protocol)サーバーを構築し、Notion API を使ってデータベース検索を行う開発でした。

技術スタック

  • CloudflareWorkers
  • MCP SDK
  • @notionhq/client(Notion 公式クライアント)

実装したかった機能

MCP として Notion のデータベースから必要な情報を検索することが目標でした。

// MCPツールとしてNotion検索機能を提供
const searchResult = await searchSites(apiKey, databaseId, "検索クエリ");

発生した問題

初期実装とエラーの発生

最初は標準的な方法で Notion クライアントを実装しました。
このコードの場合、Notion のデータベースに設定されている title プロパティに設定されている単語の記事を返却するようなコードです。
例として LINE と入力した場合、データベースから title プロパティに LINE と入っている記事を返却することが望まれます。

import { Client } from '@notionhq/client';
 
export async function searchSites(
  apiKey: string,
  databaseId: string,
  query: string,
): Promise<NotionResponse[]> {
  const client = new Client({ auth: apiKey });
  
  const response = await client.databases.query({
    database_id: databaseId,
    filter: {
      or: [
        {
          property: 'title',
          rich_text: { contains: query },
        },
        {
          property: 'description',
          rich_text: { contains: query },
        },
      ],
    },
  });
  
  return response.results;
}

以下は MCP サーバー側のサンプルコードです。
Notion API の環境変数は McpAgent<Env> と実装することで Cloudflare の .dev.vars に実装されている値を受け取ることができます。

import { McpAgent } from 'agents/mcp';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import {
  searchSites,
  extractSiteName,
  extractDescription,
} from './services/notion';
 
export class MyMCP extends McpAgent<Env> {
  server = new McpServer({
    name: 'Notion Search MCP Server',
    version: '1.0.0',
  });
 
  async init() {
    this.server.tool(
      'site_search',
      'Notionデータベースからサイト名やキーワードで検索します',
      {
        query: z.string().describe('検索したいサイト名やキーワード'),
      },
      async ({ query }) => {
        const notionApiKey = this.env.NOTION_API_KEY;
        const databaseId = this.env.NOTION_DATABASE_ID;
        const sites = await searchSites(notionApiKey, databaseId, query);
 
        if (sites.length === 0) {
          return {
            content: [
              {
                type: 'text',
                text: `「${query}」に関連するサイトが見つかりませんでした。`,
              },
            ],
          };
        }
 
        const results = sites.map((site) => ({
          name: extractSiteName(site),
          description: extractDescription(site),
        }));
 
        const resultText = results
          .map(
            (result) =>
              `**${result.name}**\n` + `説明: ${result.description}\n`,
          )
          .join('\n---\n');
 
        return {
          content: [
            {
              type: 'text',
              text: `${sites.length}件のサイトが見つかりました:\n\n${resultText}`,
            },
          ],
        };
      },
    );
  }
}
 

しかし、実際に実行すると以下のエラーが発生しました。

TypeError: Illegal invocation: function called with incorrect 'this' reference

直接API呼び出しでの動作確認

まず、Notion API 自体に問題がないことを確認するため、curl で直接 API を呼び出してテストしました。

curl -X POST https://api.notion.com/v1/databases/databaseid/query \
  -H "Authorization: Bearer $NOTION_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Notion-Version: 2022-06-28" \
  -d '{
    "filter": {
      "or": [
        {
          "property": "title",
          "rich_text": { "contains": "LINE" }
        }
      ]
    }
  }'

実行した結果、API は正常に動作し期待通りのレスポンスを取得できました。
これにより問題は Notion API 自体ではなく、Cloudflare Workers 環境でのクライアントライブラリの使用方法にあることが判明します。

検証プロセスと仮説

当初の仮説

エラーメッセージから、Notion クライアントの設定に不備があるのでは?と考えました。
curl で API が実行できていることから環境変数に不備があるとは考えづらいですが、そのあたりも視野に入れてデバッグ実行してみることにしました。

段階的なデバッグ実装

問題を特定するため、段階的にログを追加して検証しました。

export async function searchSites(
  apiKey: string,
  databaseId: string,
  query: string,
): Promise<NotionResponse[]> {
  console.log('=== Notion API デバッグ情報 ===');
  console.log(`APIキー: ${apiKey ? `${apiKey.substring(0, 10)}...` : '未定義'}`);
  console.log(`データベースID: ${databaseId}`);
  console.log(`検索クエリ: "${query}"`);
 
  try {
    console.log('--- ステップ1: Notionクライアント初期化 ---');
    const client = new Client({ auth: apiKey });
    console.log('Notionクライアント初期化成功');
 
    console.log('--- ステップ2: データベースオブジェクトの確認 ---');
    console.log('client.databases:', typeof client.databases);
    console.log('client.databases.query:', typeof client.databases.query);
 
    console.log('--- ステップ3: クエリ実行 ---');
    const response = await client.databases.query(queryParams);
    
    return response.results;
  } catch (error) {
    console.error('Notion APIエラー:', error);
    return [];
  }
}

検証で判明した事実

デバッグログを出力すると以下のような内容が記載されており、メソッド呼び出し時の this コンテキストの喪失であることが推測できました。

 [ERROR] Notion APIエラー: TypeError: Illegal invocation: function called with incorrect `this` reference. See https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors for details.

解決策の発見

Cloudflare公式ドキュメントでの調査

Cloudflare Workers の公式エラードキュメントを調査したところ、「Illegal invocation」エラーについて以下の説明を発見しました。

This is typically caused by calling a function that calls this, but the value of this has been lost.

In practice, this is often seen when destructuring runtime provided Javascript objects that have functions that rely on the presence of this, such as ctx.

developers.cloudflare.com

Errors and exceptions

Review Workers errors and exceptions.

Supabaseでの類似事例

さらに調査を進めると、Supabase クライアントでも同じ問題が発生しており、fetch.bind(globalThis) を使用する解決策が提示されていることを発見しました。

const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key', { 
  fetch: fetch.bind(globalThis) 
})
github.com

Illegal Invocation error message - Cloudflare Worker · Issue #4417 · supabase/supabase

Discussed in #4408 Originally posted by lukesnider December 9, 2021 I am attempting to create a basic cloudflare worker that will utilize the supabase postgrest client. I am simply testing with a p...

解決策の実装

Supabase の事例を参考に、Notion クライアントでも同様の対処を実装しました。
以下は修正後のコードです。

export async function searchSites(
  apiKey: string,
  databaseId: string,
  query: string,
): Promise<NotionResponse[]> {
  try {
    // fetch.bind(globalThis)でthisコンテキストを明示的に設定
    const client = new Client({ 
      auth: apiKey,
      fetch: fetch.bind(globalThis)
    });
 
    const response = await client.databases.query({
      database_id: databaseId,
      filter: {
        or: [
          {
            property: 'title',
            rich_text: { contains: query },
          },
          {
            property: 'description',
            rich_text: { contains: query },
          },
        ],
      },
    });
 
    return response.results;
  } catch (error) {
    console.error('Notion APIエラー:', error);
    return [];
  }
}

動作確認

修正後に MCP インスペクターでテストを実行したところ、データベースの中身を検索して正しく取得できました🎉
MCPインスペクターで正しく取得できたときの画像

根本原因の分析

技術的な詳細

今回の問題の根本原因はデバッグログに記載があった通り、Cloudflare Workers環境での this コンテキストの喪失でした。

問題の考察
Cloudflare Workers は V8 isolate という独自の実行環境を使用しており、標準的なブラウザや Node.js 環境とは異なる this の扱いをします。
Notion クライアントライブラリが内部で fetch を呼び出す際、this コンテキストが失われ結果として「Illegal invocation」エラーが発生したと考えられます。

解決策の考察

fetch: fetch.bind(globalThis)

bind() メソッドで fetch 関数の thisglobalThis に明示的に固定することで、Cloudflare Workers 環境でも正しいコンテキストで fetch が実行され、ライブラリ内部での this 参照エラーを回避できます。

まとめ

Cloudflare Workers 環境での Notion API クライアント使用時に発生した「Illegal invocation」エラーは、fetch.bind(globalThis) を使用することで解決できました。

ポイント

  • Cloudflare Workers は独自の実行環境のため、標準的な環境とは異なる問題が発生する可能性がある
  • エラーメッセージと公式ドキュメントを組み合わせることで、効率的に原因を特定できる
  • 他のライブラリでの類似事例を調査することで、解決策のヒントを得られる

この経験により、Cloudflare Workers 環境での外部ライブラリ使用時の注意点を深く理解できました。
同様の問題に遭遇した際は、まず fetch.bind(globalThis) を試してみることをお勧めします。

参考資料

developers.cloudflare.com

Errors and exceptions

Review Workers errors and exceptions.

github.com

Illegal Invocation error message - Cloudflare Worker · Issue #4417 · supabase/supabase

Discussed in #4408 Originally posted by lukesnider December 9, 2021 I am attempting to create a basic cloudflare worker that will utilize the supabase postgrest client. I am simply testing with a p...