April 15, 2026 • バージョン: 2026.4.11

[Firecrawl APIキーのSecretRefが解決済み構成でも401エラーを返す] - Firecrawl API Key SecretRef Returns 401 Unauthorized Despite Resolved Config Status

1PasswordのSecretRefをFirecrawlのwebFetch.apiKeyに使用すると、ゲートウェイ構成でシークレットが解決済みでマスクされているにもかかわらず、401 Unauthorizedエラーでリクエストが失敗します。

🔍 症状

主なエラーの発生状況

web_fetchリクエストがFirecrawlプラグイン経由でルーティングされ、APIキーに1PasswordのSecretRefを使用している場合、ゲートウェイがシークレットが正常に解決されたことを示しているにもかかわらず、認証エラーで失敗します。

shell

Terminal 1: 設定解決状況の確認

$ openclaw config get plugins.entries.firecrawl.config.webFetch.apiKey –verbose

sourceConfig: “op://openclaw/Firecrawl API key/credential” resolved: “••••••••••••••••” resolvedAt: “2026-04-15T10:23:41Z” status: “resolved”

shell

Terminal 2: web_fetchリクエストの実行

$ openclaw tools web-fetch “https://example.com

Error: Firecrawl API error (401): Unauthorized: Invalid token at FirecrawlProvider.fetch (firecrawl-provider.ts:147) at WebFetchTool.execute (web-fetch-tool.ts:89) at Gateway.handleToolCall (gateway.ts:204)

設定検査の不一致

ランタイム設定ビューでは、フィールドがresolvedおよびmaskedと表示されており、マテリアライゼーションレイヤーがSecretRefを受け入れたことを示唆しています。しかし、Firecrawlリクエストパスでは、以下を受け取るようです:

  • 未解決の生`op://...`文字列
  • 古いか正しくない設定スナップショット
  • 解決されたがプロバイダーインスタンスに伝播されなかった値

診断CLIコマンド

shell

プラグイン登録とランタイム状態の確認

openclaw plugins list –verbose | grep -A5 firecrawl

プロバイダーが実際に使用しているAPIキーの確認

openclaw debug provider firecrawl –show-config

シークレット解決のトレースログを有効化

OPENCLAW_LOG_LEVEL=trace openclaw gateway start 2>&1 | grep -i “firecrawl|secretref|apiKey”

環境コンテキスト

  • OpenClawバージョン: 2026.4.11
  • OS: Ubuntu (Linux kernel 5.15+)
  • ランタイムモード: ゲートウェイローカルモード
  • シークレットプロバイダー: 1Password CLI (`op://`スキーム)
  • ゲートウェイ設定: JSON設定ファイル

🧠 原因

アーキテクチャ上の障害ポイント

このバグは、ゲートウェイの設定解決レイヤーとFirecrawlプロバイダーのリクエスト実行レイヤー間の設定スナップショットの分離失敗から発生しています。

┌─────────────────────────────────────────────────────────────────────┐ │ GATEWAY PROCESS │ ├─────────────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Config Loader │───▶│ Secret Resolver │───▶│ Runtime Config │ │ │ │ (JSON/YAML) │ │ (1Password CLI) │ │ Snapshot │ │ │ └─────────────────┘ └─────────────────┘ └────────┬────────┘ │ │ │ │ │ │ snapshot │ │ │ copied │ │ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ WebFetchTool │───▶│ FirecrawlProv │───▶│ Provider Init │ │ │ │ (web-fetch) │ │ Instance │ │ (ctor/snapshot)│ │ │ └─────────────────┘ └─────────────────┘ └────────┬────────┘ │ │ │ │ │ │ uses │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Firecrawl REST │ │ │ │ API Call │ │ │ └─────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘

障害のシーケンス

  1. 設定読み込みフェーズ: ゲートウェイは`plugins.entries.firecrawl.config.webFetch.apiKey: "op://openclaw/Firecrawl API key/credential"`を含むJSON設定を読み込みます。
  2. シークレット解決フェーズ: SecretResolverサービスが1PasswordのSecretRefを処理し、プレーンテキストトークンを取得します。ランタイム設定ビューには解決済みおよびマスク済みとして正しく表示されます。
  3. プロバイダー初期化フェーズ(バグ): FirecrawlProviderがインスタンス化されるとき、シークレット解決後の設定オブジェクトではなく、古い設定スナップショットを受け取ります。プロバイダーのコンストラクターがキャプチャする内容:
    // firecrawl-provider.ts - Constructor (buggy)
    constructor(config: FirecrawlConfig) {
      // Captures config snapshot at init time
      this.apiKey = config.webFetch.apiKey;
      this.baseUrl = config.webFetch.baseUrl;
      // ...
    }
  4. リクエスト実行フェーズ: web_fetchが呼び出されると、プロバイダーはthis.apiKeyを使用しますが、これは仍然として生のSecretRef文字列"op://openclaw/Firecrawl API key/credential"を含んでいます。スナップショットがシークレット解決完了前に取得されたためです。
  5. Firecrawl APIによる拒否: Firecrawl APIはAPIキーとしてリテラルなop://...文字列を受け取り、401 Unauthorizedが発生します。

コードパスの分析

根本原因は以下のsrc/plugins/firecrawl/firecrawl-provider.ts内の初期化順序で発生します:

// Problematic initialization order
class FirecrawlProvider {
  private apiKey: string;
  private baseUrl: string;

  // Called during gateway startup, receives pre-resolution config
  constructor(config: FirecrawlConfig) {
    this.apiKey = config.webFetch.apiKey;  // ← Receives "op://..." string
    this.baseUrl = config.webFetch.baseUrl;
  }

  // This method is called at request time, but uses the captured value
  async fetch(url: string): Promise<FetchResult> {
    const headers = {
      'Authorization': `Bearer ${this.apiKey}`,  // ← Sends unresolved ref
      'Content-Type': 'application/json'
    };
    // ...
  }
}

プレーンテキストパスの動作との比較

プレーンテキスト設定を使用する場合、シークレット解決は不要です:

{
  "plugins": {
    "entries": {
      "firecrawl": {
        "config": {
          "webFetch": {
            "apiKey": "fc-actual-token-plaintext"  // ← Direct assignment
          }
        }
      }
    }
  }
}

プロバイダーは壊れたスナップショットメカニズムをバイパスして、実際のトークンを直接受け取ります。

関連するアーキテクチャパターン

この障害は、異なるライフサイクルフェーズで取得された設定スナップショットが一貫しない解決状態を含む可能性がある、SecretRefランタイムマテリアライゼーションバグ#28359と特性を共有しています。Firecrawlプロバイダーのコンストラクターは初期化時に状態をキャプチャしますが、この特定の設定パスではシークレット解決が初期化の後に発生します。

🛠️ 解決手順

オプション1: 遅延解決(推奨)

Firecrawlプロバイダーを、構築時ではなくリクエスト時にシークレット解決を実行するように修正します。

ファイル: src/plugins/firecrawl/firecrawl-provider.ts

typescript // BEFORE (buggy) class FirecrawlProvider { private apiKey: string;

constructor(config: FirecrawlConfig) { this.apiKey = config.webFetch.apiKey; // Captures at init }

async fetch(url: string): Promise { const response = await fetch(this.apiEndpoint, { headers: { ‘Authorization’: Bearer ${this.apiKey} } }); } }

// AFTER (fixed) import { SecretResolver } from ‘@openclaw/core/secrets’;

class FirecrawlProvider { private config: FirecrawlConfig; private secretResolver: SecretResolver;

constructor(config: FirecrawlConfig, secretResolver: SecretResolver) { this.config = config; this.secretResolver = secretResolver; // Inject resolver }

async fetch(url: string): Promise { // Resolve at request time, not construction time const resolvedApiKey = await this.secretResolver.resolve( this.config.webFetch.apiKey );

const response = await fetch(this.apiEndpoint, {
  headers: { 'Authorization': `Bearer ${resolvedApiKey}` }
});

} }

ファイル: src/plugins/firecrawl/plugin-registration.ts

typescript // BEFORE export function registerFirecrawlPlugin(container: PluginContainer) { container.registerSingleton(FirecrawlProvider, (config) => new FirecrawlProvider(config) ); }

// AFTER export function registerFirecrawlPlugin(container: PluginContainer) { container.registerSingleton(FirecrawlProvider, (config, secretResolver) => new FirecrawlProvider(config, secretResolver) ); }

オプション2: 解決後プロバイダーリフレッシュ

ゲートライライフサイクルでシークレット解決が完了した後、プロバイダーリフレッシュを強制します。

ファイル: src/gateway/boot.ts

typescript // Add to gateway initialization sequence async function initializeGateway(config: GatewayConfig) { // Phase 1: Load and resolve secrets const resolvedConfig = await configResolver.resolveWithSecrets(config);

// Phase 2: Initialize providers with resolved config await providerRegistry.initialize(resolvedConfig.plugins);

// Phase 3 (FIX): Refresh all provider instances to ensure they use resolved values await providerRegistry.refreshAll();

// Phase 4: Start gateway await gateway.start(); }

オプション3: 環境変数による回避策(即時の緩和策)

コードレベルの修正が利用できない場合、APIキーに環境変数注入を使用します:

ステップ1: 環境にAPIキーを設定します:

shell export FIRECRAWL_API_KEY=“your-actual-api-key-from-1password”

ステップ2: 環境変数を参照するように設定を更新します:

json { “plugins”: { “entries”: { “firecrawl”: { “enabled”: true, “config”: { “webFetch”: { “apiKey”: “${FIRECRAWL_API_KEY}”, “baseUrl”: “https://api.firecrawl.dev” } } } } } }

ステップ3: 環境変数にアクセス可能であることを確認します:

shell echo $FIRECRAWL_API_KEY

出力: your-actual-api-key-from-1password

1Password CLIを直接使用する場合:

export FIRECRAWL_API_KEY=$(op read “op://openclaw/Firecrawl API key/credential”)

ステップ4: 修正が機能することを確認

修正を適用した後、以下を実行します:

shell openclaw tools web-fetch “https://example.com

出力は401エラーではなく、取得したHTMLコンテンツである必要があります。

🧪 検証

修正前の診断

修復を試みる前に、バグが存在することを確認します:

shell

1. SecretRefが設定されていることを確認

openclaw config get plugins.entries.firecrawl.config.webFetch.apiKey

期待される出力:

op://openclaw/Firecrawl API key/credential

2. 1Password CLIが認証されているか確認

op vault list

期待される出力:

Vaults in personal:

├── openclaw

└── …

3. シークレットが存在しアクセス可能であることを確認

op item get “Firecrawl API key” –vault openclaw

期待される出力: 資格情報フィールドを含むアイテム詳細

4. web_fetchのテスト(修正前は失敗するはず)

openclaw tools web-fetch “https://httpbin.org/headers" –provider firecrawl

修正前の期待される出力:

Error: Firecrawl API error (401): Unauthorized: Invalid token

修正後の期待される出力:

{“headers”: {“Host”: “httpbin.org”, …}}

修正後の検証ステップ

ステップ1: シークレット解決の確認

shell openclaw config get plugins.entries.firecrawl.config.webFetch.apiKey –verbose

期待される出力:

{

“sourceConfig”: “op://openclaw/Firecrawl API key/credential”,

“resolved”: “••••••••••••••••”,

“status”: “resolved”,

“resolvedAt”: “2026-04-15T10:23:41Z”

}

ステップ2: プロバイダー初期化の確認

shell openclaw debug provider firecrawl –show-config

期待される出力: apiKeyフィールドは”••••••••••••••••"(マスク済み)と表示

生"op://…“文字列ではないはず

ステップ3: リクエスト実行の確認

shell

Authorizationヘッダーをエコーする単純なエンドポイントでテスト

openclaw tools web-fetch “https://httpbin.org/headers" –provider firecrawl

期待される出力: Hostヘッダーが表示されたJSONを返す

401エラーは発生しないはず

ステップ4: Firecrawl API相互作用の確認(デバッグモード)

shell OPENCLAW_LOG_LEVEL=debug openclaw tools web-fetch “https://example.com” –provider firecrawl 2>&1 | grep -E “(firecrawl|Firecrawl|Authorization|Authorization: Bearer)”

期待される出力: 解決されたBearerトークンが送信されていることを表示

Authorizationヘッダーに"op://…“が表示されないはず

ステップ5: 自動テストスイート

利用可能な場合、Firecrawl[Test=“true” enabled=“true”] plugin=[Test=“true” enabled=“true”] suiteを実行します:

shell openclaw test –plugin firecrawl –secretref-mode

期待される出力: SecretRefシナリオを含むすべてのテストがパス

終了コードの確認

shell

成功ケース

openclaw tools web-fetch “https://example.com” –provider firecrawl echo “Exit code: $?” # 0であるべき

失敗ケース(修正前)

openclaw tools web-fetch “https://example.com” –provider firecrawl echo “Exit code: $?” # ゼロ以外であるべき(通常は1)

⚠️ よくある落とし穴

環境固有のトラップ

  • 1Password CLIが認証されていない:
    SecretRefを使用する前に、1Password CLIがサインインしていることを確認します:
    # Check authentication status
    op account list
    

    If not authenticated, sign in:

    op signin

    Verify access to the vault:

    op vault list

  • ボールトアクセスの権限:
    1PasswordアカウントはSecretRefで参照されているボールトにアクセスできる必要があります:
    # Verify vault permissions
    op account get
    

    Ensure the vault “openclaw” is accessible

    op vault get openclaw

  • サービスアカウントと個人アカウント:
    ゲートウェイをサービスとして実行している場合、サービスが1Passwordにアクセスできることを確認してください。個人のセッションだけに依存しないでください:
    # For systemd service, set up 1Password session via environment or systemd-run
    # Avoid relying on personal 1Password session tokens

設定の落とし穴

  • 設定内の二重解決:
    FIRECRAWL_API_KEYを環境変数として設定し、設定でも参照している場合、設定では$FIRECRAWL_API_KEYではなく${FIRECRAWL_API_KEY}構文を使用してください:
    # Wrong - will be passed literally
    "apiKey": "$FIRECRAWL_API_KEY"
    

    Correct - will be resolved

    “apiKey”: “${FIRECRAWL_API_KEY}"

  • SecretRef内の空白:
    1Password SecretRefには先頭または末尾の空白を含めないでください:
    # Wrong - may cause resolution failure
    "apiKey": "op://openclaw/Firecrawl API key/credential "
    

    Correct

    “apiKey”: “op://openclaw/Firecrawl API key/credential”

  • アイテム名内の特殊文字:
    1Passwordアイテム名に特殊文字が含まれている場合は、URLエンコードしてください:
    # For item named "Firecrawl API (Dev & Prod)"
    "apiKey": "op://openclaw/Firecrawl%20API%20(Dev%20%26%20Prod)/credential"

ランタイムの落とし穴

  • ゲートウェイの再起動が必要:
    SecretRefへの変更は、ゲートウェイが他の設定変更をホットリロードする場合でも、ゲートウェイの再起動が必要です:
    # Required after changing SecretRef
    openclaw gateway restart
    

    Not sufficient:

    openclaw config reload # Only reloads non-secret config

  • プロバイダーシングルトンの動作:
    シングルトンプロバイダーパターンのため、シークレット解決前にキャッシュされたプロバイダーインスタンスは、ゲートウェイが完全に再起動するまで古い値の使用を続けます:
    # Verify no stale instances
    openclaw debug provider firecrawl --status
    

    Should show: “Initialized: true”, “Config Snapshot: fresh”

  • Dockerボリュームマウントの考慮事項:
    Dockerで実行している場合、1Passwordソケット/資格情報が適切にマウントされていることを確認してください:
    # Wrong - credentials not available in container
    docker run openclaw:latest
    

    Correct - mount 1Password socket

    docker run -v openclaw_config:/app/config -e OP_SESSION=… openclaw:latest

macOS固有の問題

  • キーチェインアクセスのプロンプト:
    1Password CLIは非対話的に実行する場合にキーチェインアクセスを要求する可能性があります。CI/CD環境ではサービスアカウントでop signin --accountを使用してください。
  • SSHエージェント転送:
    1Password資格情報がSSH経由で転送される場合、OP_SESSION環境変数も転送されていることを確認してください。

Windows固有の問題

  • パス区切り文字:
    SecretRefパスはWindowsでもスラッシュ前方を使用します。バックスラッシュに変換しないでください。
  • WSL2環境変数:
    WSL2でOpenClawを実行し、Windows 1Passwordを使用している場合、WSL2内に1Password CLIをセットアップし、Windowsホストとは別の場所で認証してください。

🔗 関連するエラー

主な関連問題

  • #28359 - SecretRefランタイムマテリアライゼーショ的不整合
    SecretRef値が異なる解決状態で設定スナップショットにキャプチャされる可能性がある方法を説明する過去のイシューです。Firecrawlバグはこのより広範なパターンの特定の現れです。

症状として関連するエラー

  • 401 Unauthorized: Invalid token
    汎用的な認証失敗です。この文脈では、リテラルop://...文字列がBearerトークンとして送信されることで発生しています。
  • SecretRef resolution failed: Item not found
    SecretRefで指定された1Passwordアイテムまたはボールトが存在しないかアクセスできないことを示します。Firecrawlバグとは異なり、ここでは解決自体が失敗しています。
  • Plugin initialization failed: Config snapshot mismatch
    プラグインが初期化フェーズ全体で一貫しない設定を受け取ったことを示す内部OpenClawエラーです。Firecrawlプロバイダーの初期化順序が変更された場合、ゲートウェイ起動中に表示されることがあります。
  • Gateway config validation error: Invalid SecretRef format
    SecretRefがop://vault/item/fieldパターンに準拠していない場合のスキーマ検証失敗です。

エラーコードリファレンス

エラーコードカテゴリ説明
SECRET_REF_001解決SecretRef形式検証失敗
SECRET_REF_002解決1Password CLIが認証されていない
SECRET_REF_003解決ターゲットアイテムまたはボールトにアクセス不可
SECRET_REF_004マテリアライゼーション解決された値がコンシューマーに伝播されなかった
PLUGIN_AUTH_401認証プラグインが無効な資格情報を受け取った
PLUGIN_INIT_001初期化プロバイダーが古い設定スナップショットで初期化された

プラグイン横断的な考慮事項

同じスナップショット分離パターンが影響を与える可能性のある他のプラグイン:

  • SecretRef経由でAPIキーまたはトークンを受け入れる
  • ゲートウェイ起動中にプロバイダーを初期化する
  • シングルトンプロバイダーインスタンスを使用する

既知の潜在的に影響を受ける可能性のあるプラグイン:

  • plugins.entries.anthropic.config.apiKey
  • plugins.entries.google.config.credentials
  • plugins.entries.aws.config.accessKeyId
  • plugins.entries.azure.config.subscriptionKey

Firecrawl[Test=“true” enabled=“true”] plugin=[Test=“true” enabled=“true”] suiteを実行して、同じイシューをテストしてください。

エビデンスとソース

このトラブルシューティングガイドは、FixClaw Intelligence パイプラインによってコミュニティの議論から自動的に合成されました。