Slack プラグインが SecretRef トークンを使用する場合に agents コマンドで読み込みに失敗する: PluginLoadFailureError
Slack チャネルトークンが SecretRef オブジェクトとして保存されている場合、Slack プラグインは登録時にそれらを事前解決しようとし、CLI がすべての agents/* コマンドで致命的な PluginLoadFailureError が発生してクラッシュします。
🔍 症状
SlackチャンネルのトークンがSecretRefオブジェクトとして設定されている場合、openclaw agents名前空間配下のすべてのCLIコマンドが、直ちに致命的なプラグインロードエラーで終了します。コマンドロジックが実行される前にエラーが発生するため、エージェント管理全体のCLIが使用できなくなります。
主なエラー出力
$ openclaw agents list
[plugins] slack failed during register from .../extensions/slack/index.js:
Error: channels.slack.accounts.default.botToken: unresolved SecretRef "file:local:/SLACK_BOT_TOKEN".
Resolve this command against an active gateway runtime snapshot before reading it.
[openclaw] Failed to start CLI: PluginLoadFailureError: plugin load failed: slack: ...
$
$ echo $?
1影響を受けるその他のコマンド
$ openclaw agents add myagent --workspace ~/.openclaw/workspace-myagent
# Same PluginLoadFailureError output, exit code 1
$ openclaw agents remove myagent
# Same PluginLoadFailureError output, exit code 1
$ openclaw agents status myagent
# Same PluginLoadFailureError output, exit code 1버그を引き起こす設定パターン
以下のopenclaw.json設定セグメントが障害の原因となります:
json { “channels”: { “slack”: { “accounts”: { “default”: { “botToken”: { “source”: “file”, “provider”: “local”, “id”: “/SLACK_BOT_TOKEN” }, “appToken”: { “source”: “file”, “provider”: “local”, “id”: “/SLACK_APP_TOKEN” } } } } } }
ゲートウェイの動作との違い
実行中のゲートウェイプロセスはこの問題による影響を受けません。ゲートウェイは解決済みシークレットのメモリ内スナップショットを保持し、Slackイベントを正常に処理します。CLI(別個のプロセス)のみが失敗するのは、ランタイムシークレット解決コンテキストにアクセスせずにopenclaw.jsonから生の設定を読み取るためです。
🧠 原因
実行コールチェーン
クラッシュは、プラグイン登録時の先行解決の特定のシーケンスから発生します:
- コマンド実行パス:
command-execution-startup.jsがすべてのagentsサブコマンドに対してloadPlugins: "always"を設定し、すべての呼び出しで完全なプラグイン初期化を強制します。 - 厳格なエラー処理:
runtime-registry-loader.jsはthrowOnLoadError: trueでCLIプラグイン読み込みを実行し、任意のプラグイン登録エラーを致命的な状態に変換します。 - ルート登録が解決をトリガー:
extensions/slack/index.jsがregisterフック中にregisterSlackPluginHttpRoutes()を呼び出します。 - 先行トークンアクセス:
registerSlackPluginHttpRoutes()がデフォルトアカウント含め無条件でresolveSlackAccount({cfg, accountId})を呼び出します。 - SecretRefが未解決エラーをスロー:
accounts.js→resolveSlackBotToken(merged.botToken, …)→normalizeResolvedSecretInputString()が未解決のSecretRefオブジェクトを検出し、致命的なエラーをスローします。
アーキテクチャ上の不整合
Slackプラグインは、チャネルプラグインに求められる遅延解決の原則に違反しています:
- 期待される動作: プラグイン登録ではルートとハンドラーを登録し、準備だけを行います。実際のトークン解決は、インバウンドイベント処理時(つまり、HTTPリクエストハンドラー内)に行うべきです。その時点でゲートウェイヤンタイムスナップショットが利用可能になります。
- 実際の動作: リクエストが処理される前に、ルート登録中に同期的にトークン解決が発生します。
CLIがシークレットを解決できない理由
CLIプロセスは、以下の特徴を持つスタンドアロンの実行ファイルです:
- ディスクから直接
openclaw.jsonを読み取ります。 - アクティブなゲートウェイヤンタイム接続がありません。
- ゲートウェイのシークレット解決スナップショットにアクセスできません。
- 具体的な文字列値に解決されていない
SecretRefに遭遇すると失敗します。
効果が不十分な回避策
| 回避策 | 失敗の理由 |
|---|---|
SLACK_BOT_TOKEN環境変数を設定 | accounts.jsは環境フォールバックを確認する前からSecretRef構造を評価します。環境解決が発生する前にスローされます。 |
channels.slack.enabled: falseを設定 | registerSlackPluginHttpRoutesが無条件でDEFAULT_ACCOUNT_IDを反復処理し、登録時にenabledフラグを無視します。 |
plugins.allowから“slack”を削除 | プラグインはchannels.slack設定ブロックによってもトリガーされます。allowlistからの削除だけでは読み込み防止に不十分です。 |
OPENCLAW_DISABLE_BUNDLED_PLUGINS=1 | 整个channels設定を無効化し、channels.slack: unknown channel idを報告します。 |
🛠️ 解決手順
オプションA:一時的な回避策(即時の救済)
CLIコマンドの実行前にSecretRefオブジェクトをダミーの文字列値に置き換え、元の設定を復元します。
変更前 (openclaw.json):
"botToken": { "source": "file", "provider": "local", "id": "/SLACK_BOT_TOKEN" },
"appToken": { "source": "file", "provider": "local", "id": "/SLACK_APP_TOKEN" }変更後(一時的):
"botToken": "xoxb-placeholder-do-not-use",
"appToken": "xapp-placeholder-do-not-use"必要なCLIコマンドを実行します:
openclaw agents add myagent --workspace ~/.openclaw/workspace-myagent実行後、直ちにopenclaw.jsonの元のSecretRefオブジェクトを復元します。
オプションB:設定ベースの回避策
Slack設定を完全に除外したCLI専用のopenclaw.jsonを作成します。
- 現在の設定をバックアップします:
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.gateway- チャネル定義なしでCLI固有の設定を作成します:
cat > ~/.openclaw/openclaw-cli.json << 'EOF'
{
"version": "2",
"workspace": {
"default": "~/.openclaw/workspace-default"
},
"plugins": {
"allow": ["core", "agents"]
}
}
EOF- 環境変数でCLI固有の設定を使用します:
OPENCLAW_CONFIG=~/.openclaw/openclaw-cli.json openclaw agents listオプションC:コード修正(恒久的な解決策)
Slackプラグインのregisterフックを修正して、トークン解決を遅延させます。
ファイル: extensions/slack/index.js
変更前(現在の問題のあるコード):
function registerSlackPluginHttpRoutes(cfg) {
// Eagerly resolve account during registration
const account = resolveSlackAccount({ cfg, accountId: DEFAULT_ACCOUNT_ID });
router.post('/events', async (req, res) => {
// Handle Slack events...
});
}変更後(遅延解決):
function registerSlackPluginHttpRoutes(cfg) {
// Register route unconditionally; resolve lazily on each request
router.post('/events', async (req, res) => {
// Resolve account only when processing an actual event
const account = resolveSlackAccount({ cfg, accountId: DEFAULT_ACCOUNT_ID });
// Handle Slack events...
});
}アカウントトークンにアクセスする他のルート(/interactions、/commandsなど)にも同じパターンを適用します。
🧪 検証
回避策の検証(オプションA)
- 元の設定に
SecretRefオブジェクトが含まれていることを確認します:
$ grep -A5 '"botToken"' ~/.openclaw/openclaw.json
"botToken": {
"source": "file",
"provider": "local",
"id": "/SLACK_BOT_TOKEN"
},- プレースホルダー文字列に一時的に置き換え、コマンドを実行します:
$ sed -i '' 's/"botToken": { "source": "file", "provider": "local", "id": "\/SLACK_BOT_TOKEN" }/"botToken": "xoxb-placeholder"/' ~/.openclaw/openclaw.json
$ sed -i '' 's/"appToken": { "source": "file", "provider": "local", "id": "\/SLACK_APP_TOKEN" }/"appToken": "xapp-placeholder"/' ~/.openclaw/openclaw.json
$ openclaw agents list
[agent list output]
$ echo $?
0- 元の設定を復元します:
$ cp ~/.openclaw/openclaw.json.gateway ~/.openclaw/openclaw.json
# 復元の確認
$ grep -A5 '"botToken"' ~/.openclaw/openclaw.json | head -6
"botToken": {
"source": "file",
"provider": "local",
"id": "/SLACK_BOT_TOKEN"
},恒久的な修正の検証(オプションC)
extensions/slack/index.jsにコード修正を適用した後:
- OpenClaw CLIを再ビルドまたは再インストールします:
$ npm run build # or the appropriate build command for your installation
$ openclaw --version
openclaw/2026.4.9 darwin-x64 node-v22.8.0SecretRef設定がそのままの状態でagentsコマンドが成功することを確認します:
$ openclaw agents list
[agent list output]
$ echo $?
0- ゲートウェイがSlackイベントを正常に処理し続けていることを確認します(実行中の場合):
$ curl -X POST http://localhost:3000/slack/events \
-H "Content-Type: application/json" \
-d '{"type": "url_verification", "challenge": "test"}'
{"challenge": "test"}- ログに残留エラーがないことを確認します:
$ tail -n 50 ~/.openclaw/logs/gateway.log | grep -i "secret\|token\|resolve"
# エラーメッセージは表示されないはずです⚠️ よくある落とし穴
落とし穴1:設定を復元し忘れる
プレースホルダーの回避策を使用した後、ユーザーが元のSecretRef設定を復元し忘れることがよくあります。これにより、次回の再起動時に実行中のゲートウェイが無効なプレースホルダートークンをリロードすることになります。
緩和策:常に変更前にバックアップを取り、アトミック操作を使用します:
# アトミックな交換とバックアップ
cp ~/.openclaw/openclaw.json ~/.openclaw/openclaw.json.backup && \
sed -i 's/placeholder/dummy/' ~/.openclaw/openclaw.json && \
openclaw agents list && \
mv ~/.openclaw/openclaw.json.backup ~/.openclaw/openclaw.json落とし穴2:複数のCLI設定による混乱
別のCLI設定を作成したユーザーは、CLIの動作を期待してゲートウェイ設定を誤って変更したり、その逆したりすることがあります。
緩和策:常に明示的なOPENCLAW_CONFIG環境変数を使用し、アクティブな設定を確認します:
$ OPENCLAW_CONFIG=~/.openclaw/openclaw-cli.json openclaw config show | grep workspace落とし穴3:enabled: falseでプラグイン読み込みが防止されると誤解する
enabledフラグに関係なくSlackプラグインは読み込まれます。これは、プラグインがplugins.allowリストとchannels.slack設定ブロックの両方でトリガーされるためです。
緩和策:CLIコンテキストでプラグイン読み込みをスキップするためにenabled: falseに依存しないでください。
落とし穴4:環境変数の回避策が不十分
SLACK_BOT_TOKENを環境変数として設定しても、SecretRefエラーをバイパスできません。プラグインは環境フォールバックを確認する前からSecretRef構造を評価するからです。
緩和策:代わりにプレースホルダーの回避策または別のCLI設定アプローチを使用してください。
落とし穴5:Docker/コンテナ環境
コンテナ化されたデプロイメントでは、file:localシークレットプロバイダーがSecretRefで指定されたホストファイルシステムパスにアクセスできない場合があります。
緩和策:シークレットディレクトリをコンテナにマウントします:
docker run --rm \
-v $HOME/.openclaw:/root/.openclaw \
-v /run/secrets:/run/secrets:ro \
openclaw agents list落とし穴6:Windowsのパス区切り文字
Windowsでは、SecretRefIDはスラッシュ(/SLACK_BOT_TOKEN)を使用しますが、これは一部の設定でWindowsのパス解析と競合する可能性があります。
緩和策:Windows互換のシークレットIDを使用するか、file:localプロバイダーを適切なパスマッピングで設定してください。
🔗 関連するエラー
PluginLoadFailureError— 一般的なプラグイン読み込み失敗。この文脈では、Slackプラグインが登録時の先行トークン解決により特にトリガーされます。終了コード: 1。channels.slack.accounts.default.botToken: unresolved SecretRef—SecretRefオブジェクトが文字列値に解決されていない場合にnormalizeResolvedSecretInputString()がスローする固有のエラー。channels.slack: unknown channel id— バンドルされたプラグインが無効になっているがchannels.slack設定が存在する場合に発生するエラー。OPENCLAW_DISABLE_BUNDLED_PLUGINS環境変数の設定ミスを示します。SecretRefresolution errors in other plugins — 他のチャネルプラグイン(Microsoft Teams、Discordなど)にも、Slackと同じ登録パターンに従う場合、同様の先行解決の問題が存在する可能性があります。OPENCLAW_CONFIGnot found — 指定された設定ファイルパスが存在しない場合のエラー。パスが絶対パスであること、または正しい作業ディレクトリからの相対パスであることを確認してください。throwOnLoadError: truein non-CLI contexts —throwOnLoadError: trueでプラグイン読み込みを呼び出す任意のコードパスが、CLIだけでなくこの同じ失敗モードを示します。runtime-registry-loader.jsのすべての呼び出しサイトをレビューしてください。