power-automate-debug について
FlowStudio MCPを通じて、Power Automateのクラウドフローの失敗を調べる診断スキルです。エージェント向けに、実行データから原因を特定し、修正と再検証を進める手順を示します。
主な機能
- 01フローと失敗実行の特定
- 02アクションの入出力確認
- 03上流データと式の追跡
- 04フロー修正と検証
活用シーン
- 01null値や誤ったフィールドの診断
- 02HTTPアクション失敗の原因調査
- 03失敗実行を再送して修正をテスト
MCP 対応
有効なJWTで接続できるFlowStudio MCPサーバーが必要です。フローや実行に関するツールを使い、失敗の調査、フローの更新、実行の再送を行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ninihen1/ power-automate-mcp-skills --skill power-automate-debugインストール後、Claude Code で/power-automate-debugを入力して読み込まれたか確認します。
同梱ファイルreferences/common-errors.mdreferences/debug-workflow.md
詳細
FlowStudio MCP を使用した Power Automate のデバッグ
FlowStudio MCP サーバーを通じて、失敗した Power Automate クラウドフローを調査するための、段階的な診断プロセスです。
実際のデバッグ例:子フローの式エラー | フローのバグではなくデータ入力の問題 | null 値が子フローをクラッシュさせる
前提条件:有効な JWT で FlowStudio MCP サーバーに接続できる必要があります。
接続の設定については、power-automate-mcp スキルを参照してください。
https://mcp.flowstudio.app で登録してください
信頼できる情報源
まず必ず
list_skills/tool_searchを呼び出し、利用可能なツール名とパラメーターのスキーマを確認してください。ツール名とパラメーターは、サーバーのバージョンによって変わる場合があります。 このスキルでは、ツールのスキーマからは分からない応答の形式、動作に関する注意事項、診断パターンを説明します。このドキュメントの内容がtool_searchまたは実際の API 応答と異なる場合は、API の情報を優先してください。
Python ヘルパー
import json, urllib.request
MCP_URL = "https://mcp.flowstudio.app/mcp"
MCP_TOKEN = "<YOUR_JWT_TOKEN>"
def mcp(tool, **kwargs):
payload = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": tool, "arguments": kwargs}}).encode()
req = urllib.request.Request(MCP_URL, data=payload,
headers={"x-api-key": MCP_TOKEN, "Content-Type": "application/json",
"User-Agent": "FlowStudio-MCP/1.0"})
try:
resp = urllib.request.urlopen(req, timeout=120)
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", errors="replace")
raise RuntimeError(f"MCP HTTP {e.code}: {body[:200]}") from e
raw = json.loads(resp.read())
if "error" in raw:
raise RuntimeError(f"MCP error: {json.dumps(raw['error'])}")
return json.loads(raw["result"]["content"][0]["text"])
ENV = "<environment-id>" # e.g. Default-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ステップ 1 — フローを特定する
result = mcp("list_live_flows", environmentName=ENV)
# Returns a wrapper object: {mode, flows, totalCount, error}
target = next(f for f in result["flows"] if "My Flow Name" in f["displayName"])
FLOW_ID = target["id"] # plain UUID — use directly as flowName
print(FLOW_ID)
ステップ 2 — 失敗した実行を見つける
runs = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=5)
# Returns direct array (newest first):
# [{"name": "08584296068667933411438594643CU15",
# "status": "Failed",
# "startTime": "2026-02-25T06:13:38.6910688Z",
# "endTime": "2026-02-25T06:15:24.1995008Z",
# "triggerName": "manual",
# "error": {"code": "ActionFailed", "message": "An action failed..."}},
# {"name": "...", "status": "Succeeded", "error": null, ...}]
for r in runs:
print(r["name"], r["status"], r["startTime"])
RUN_ID = next(r["name"] for r in runs if r["status"] == "Failed")
ステップ 3 — 最上位のエラーを取得する
重要:
get_live_flow_run_errorは、どのアクションが失敗したかを示します。get_live_flow_run_action_outputsは、なぜ失敗したかを示します。両方を呼び出す必要があります。 エラーだけで調査を終えてはいけません。ActionFailed、NotSpecified、InternalServerErrorなどのエラーコードは汎用的なラッパーです。根本原因(誤ったフィールド、null 値、HTTP 500 の応答本文、スタックトレース)は、アクションの入力と出力にしか表示されません。
err = mcp("get_live_flow_run_error",
environmentName=ENV, flowName=FLOW_ID, runName=RUN_ID)
# Returns:
# {
# "runName": "08584296068667933411438594643CU15",
# "failedActions": [
# {"actionName": "Apply_to_each_prepare_workers", "status": "Failed",
# "error": {"code": "ActionFailed", "message": "An action failed..."},
# "startTime": "...", "endTime": "..."},
# {"actionName": "HTTP_find_AD_User_by_Name", "status": "Failed",
# "code": "NotSpecified", "startTime": "...", "endTime": "..."}
# ],
# "allActions": [
# {"actionName": "Apply_to_each", "status": "Skipped"},
# {"actionName": "Compose_WeekEnd", "status": "Succeeded"},
# ...
# ]
# }
# failedActions is ordered outer-to-inner. The ROOT cause is the LAST entry:
root = err["failedActions"][-1]
print(f"Root action: {root['actionName']} → code: {root.get('code')}")
# allActions shows every action's status — useful for spotting what was Skipped
# See common-errors.md to decode the error code.
ステップ 4 — 失敗したアクションの入力と出力を調べる
これが最も重要なステップです。
get_live_flow_run_errorから分かるのは、汎用的なエラーコードだけです。実際のエラーの詳細(HTTP ステータスコード、応答本文、スタックトレース、null 値)は、アクションの実行時の入力と出力にあります。失敗したアクションを特定したら、直ちに必ず調べてください。
# Get the root failing action's full inputs and outputs
root_action = err["failedActions"][-1]["actionName"]
detail = mcp("get_live_flow_run_action_outputs",
environmentName=ENV,
flowName=FLOW_ID,
runName=RUN_ID,
actionName=root_action)
if len(detail) > 1:
print(f"{root_action} returned {len(detail)} repetitions; inspect iteration indexes")
out = detail[0] if detail else {}
print(f"Action: {out.get('actionName')}")
print(f"Status: {out.get('status')}")
# For HTTP actions, the real error is in outputs.body
if isinstance(out.get("outputs"), dict):
status_code = out["outputs"].get("statusCode")
body = out["outputs"].get("body", {})
print(f"HTTP {status_code}")
print(json.dumps(body, indent=2)[:500])
# Error bodies are often nested JSON strings — parse them
if isinstance(body, dict) and "error" in body:
err_detail = body["error"]
if isinstance(err_detail, str):
err_detail = json.loads(err_detail)
print(f"Error: {err_detail.get('message', err_detail)}")
# For expression errors, the error is in the error field
if out.get("error"):
print(f"Error: {out['error']}")
# Also check inputs — they show what expression/URL/body was used
if out.get("inputs"):
print(f"Inputs: {json.dumps(out['inputs'], indent=2)[:500]}")
エラーコードだけでは分からない、アクションの出力から分かること
get_live_flow_run_error のエラーコード | get_live_flow_run_action_outputs から分かること |
|---|---|
ActionFailed | 実際に失敗した入れ子のアクションと、その HTTP 応答 |
NotSpecified | 実際のエラーを含む HTTP ステータスコードと応答本文 |
InternalServerError | サーバーのエラーメッセージ、スタックトレース、または API エラーの JSON |
InvalidTemplate | 失敗した正確な式と、null または型が誤った値 |
BadRequest | 送信されたリクエスト本文と、サーバーがそれを拒否した理由 |
Foreach の反復
actionName が foreach 内のアクションを指している場合、出力ツールはそのアクションのすべての反復を返すことがあります。各項目には、ループ名とゼロ始まりの itemIndex を含む repetitionIndexes が含まれる場合があります。疑わしい項目を見つけたら、iterationIndex を使って該当する反復を調べてください。
all_reps = mcp("get_live_flow_run_action_outputs",
environmentName=ENV,
flowName=FLOW_ID,
runName=RUN_ID,
actionName=root_action)
for rep in all_reps[:10]:
print(rep.get("repetitionIndexes"), rep.get("status"), rep.get("error"))
one_rep = mcp("get_live_flow_run_action_outputs",
environmentName=ENV,
flowName=FLOW_ID,
runName=RUN_ID,
actionName=root_action,
iterationIndex=3)
証拠となる Compose の前後アクション
コネクタの動作が不確かな場合は、リスクのあるアクションの前に Compose_*_Request を、後に Compose_*_Result を追加し、結果アクションが Succeeded と Failed のどちらでも実行されるようにします。これにより、再デプロイせずに、今後のデバッグに役立つ明確なペイロードのスナップショットを取得できます。これらの前後アクションには、シークレットや長いバイナリペイロードを含めないでください。
例:HTTP アクションが 500 を返す場合
Error code: "InternalServerError" ← this tells you nothing
Action outputs reveal:
HTTP 500
body: {"error": "Cannot read properties of undefined (reading 'toLowerCase')
at getClientParamsFromConnectionString (storage.js:20)"}
← THIS tells you the Azure Function crashed because a connection string is undefined
例:null による式エラー
Error code: "BadRequest" ← generic
Action outputs reveal:
inputs: "body('HTTP_GetTokenFromStore')?['token']?['access_token']"
outputs: "" ← empty string, the path resolved to null
← THIS tells you the response shape changed — token is at body.access_token, not body.token.access_token
ステップ 5 — フロー定義を読む
defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
actions = defn["properties"]["definition"]["actions"]
print(list(actions.keys()))
定義内で失敗したアクションを見つけます。想定されるデータを理解するため、inputs の式を確認してください。
ステップ 6 — 失敗箇所からさかのぼる
失敗したアクションの入力が上流のアクションを参照している場合は、 それらも調べます。問題のあるデータの発生元が見つかるまで、チェーンを さかのぼってください。
# Inspect multiple actions leading up to the failure
for action_name in [root_action, "Compose_WeekEnd", "HTTP_Get_Data"]:
result = mcp("get_live_flow_run_action_outputs",
environmentName=ENV,
flowName=FLOW_ID,
runName=RUN_ID,
actionName=action_name)
out = result[0] if result else {}
print(f"\n--- {action_name} ({out.get('status')}) ---")
print(f"Inputs: {json.dumps(out.get('inputs', ''), indent=2)[:300]}")
print(f"Outputs: {json.dumps(out.get('outputs', ''), indent=2)[:300]}")
⚠️ 配列を処理するアクションの出力ペイロードは、非常に大きくなることがあります。 出力する前に、必ずスライスしてください(例:
[:500])。
ヒント:問題のあるデータを生成したアクションが分からない場合は、
actionNameを省略すると、トップレベルのアクションが一覧表示されます。foreach 内のアクションを選んだら、iterationIndexを指定して、すべての反復をコンテキストに読み込まないようにしてください。
ステップ 7 — 根本原因を特定する
式のエラー(例:null に対する split)
エラーに InvalidTemplate または関数名が含まれている場合:
- 定義内で該当するアクションを見つけます
- そのアクションが読み取る上流のアクションまたは式を確認します
- その上流アクションの出力に null やフィールドの欠落がないか調べます
# Example: action uses split(item()?['Name'], ' ')
# → null Name in the source data
result = mcp("get_live_flow_run_action_outputs", ..., actionName="Compose_Names")
if not result:
print("No outputs returned for Compose_Names")
names = []
else:
names = result[0].get("outputs", {}).get("body") or []
nulls = [x for x in names if x.get("Name") is None]
print(f"{len(nulls)} records with null Name")
フィールドパスの誤り
式 triggerBody()?['fieldName'] が null を返す場合、fieldName が誤っています。
トリガーの出力を調べて、実際のフィールド名を確認してください:
result = mcp("get_live_flow_run_action_outputs", ..., actionName="<trigger-action-name>")
print(json.dumps(result[0].get("outputs"), indent=2)[:500])
エラーを返す HTTP アクション
エラーコードが InternalServerError または NotSpecified の場合は、実際の HTTP ステータスとレスポンス本文を確認するために、必ずアクションの出力を調べてください:
result = mcp("get_live_flow_run_action_outputs", ..., actionName="HTTP_Get_Data")
out = result[0]
print(f"HTTP {out['outputs']['statusCode']}")
print(json.dumps(out['outputs']['body'], indent=2)[:500])
接続/認証の失敗
ConnectionAuthorizationFailed がないか確認してください。接続の所有者は、フローの実行に使われているサービスアカウントと一致する必要があります。API 経由では修正できません。PA デザイナーで修正してください。
Outlook のユーザー選択失敗(DynamicListValuesUndefinedOrInvalid)
GetEmailsV3 などの Outlook アクションでは、mailboxAddress、to、cc、from といったパラメーターのドロップダウンが builtInOperation:AadGraph.GetUsers によって提供されます。しかし、この操作は PA の listEnum レイヤーで壊れており、常に DynamicListValuesUndefinedOrInvalid を返します。エージェントが update_live_flow 経由で Outlook アクションを再構築または変更し、動的なオプションからユーザーを解決しようとすると、この問題が発生します。AadGraph を再試行して解決しようとしないでください。代わりに shared_office365users.SearchUserV2 に切り替えてください(同じ AAD ユーザー形式が返されます)。describe_live_connector を使って、該当するパラメーターに構造化された fallback があるかどうかを確認し、壊れている AadGraph 操作ではなく shared_office365users.SearchUserV2 に対して get_live_dynamic_options を呼び出してください。ドロップダウンのオプションではなく動的なフィールドスキーマを取得する場合は、describe_live_connector が返すメタデータを使って get_live_dynamic_properties を呼び出してください。
ステップ 8 — 修正を適用する
式/データの問題の場合:
defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
acts = defn["properties"]["definition"]["actions"]
# Example: fix split on potentially-null Name
acts["Compose_Names"]["inputs"] = \
"@coalesce(item()?['Name'], 'Unknown')"
conn_refs = defn["properties"]["connectionReferences"]
result = mcp("update_live_flow",
environmentName=ENV,
flowName=FLOW_ID,
definition=defn["properties"]["definition"],
connectionReferences=conn_refs)
print(result.get("error")) # None = success
⚠️
update_live_flowは常にerrorキーを返します。 値がnull(Python のNone)の場合は成功です。
ステップ 9 — 修正を検証する
HTTP トリガーに限らず、どのフローのテストにも
resubmit_live_flow_runを使ってください。resubmit_live_flow_runは、以前の実行を元のトリガーペイロードで再生します。これは、Recurrence、SharePoint の「アイテムが作成されたとき」、コネクタの Webhook、Button トリガー、HTTP トリガーなど、すべてのトリガーの種類で機能します。ユーザーに手動でフローをトリガーするよう依頼したり、次のスケジュール実行を待ったりする必要はありません。
resubmitを利用できないのは、一度も実行されていない新規フローだけです。再生できる過去の実行がありません。
# Resubmit the failed run — works for ANY trigger type
resubmit = mcp("resubmit_live_flow_run",
environmentName=ENV, flowName=FLOW_ID, runName=RUN_ID)
print(resubmit) # {"resubmitted": true, "triggerName": "..."}
# Wait ~30 s then check
import time; time.sleep(30)
new_runs = mcp("get_live_flow_runs", environmentName=ENV, flowName=FLOW_ID, top=3)
print(new_runs[0]["status"]) # Succeeded = done
resubmit と trigger の使い分け
| シナリオ | 使用するもの | 理由 |
|---|---|---|
| どのフローでも修正をテストする場合 | resubmit_live_flow_run | 失敗の原因となったトリガーペイロードをそのまま再生するため、検証に最適です |
| Recurrence/スケジュールされたフロー | trigger_live_flow(body なし) | ポータルの「フローを実行」ボタンのように、今すぐ実行します。resubmit は過去の実行データを再生します |
| SharePoint/コネクタのトリガー | resubmit_live_flow_run | 実際の SP アイテムを作成せずにトリガーすることはできません |
| 独自のテストペイロードを使う HTTP、Button、または PowerApps トリガー | trigger_live_flow | 元の実行とは異なるデータを送信する必要がある場合に使用します |
| 一度も実行されていない新規フロー | trigger_live_flow | resubmit できる過去の実行がありません |
カスタムペイロードを使用した HTTP、Button、PowerApps フローのテスト
Request トリガー(HTTP 要求、手動の Button、PowerApps)を使用するフローで、元の実行とは異なるペイロードを送信する必要がある場合は、trigger_live_flow を使用します。すべての種類で、トリガーの入力を body として渡します。
# First inspect what the trigger expects — read directly from the flow definition
defn = mcp("get_live_flow", environmentName=ENV, flowName=FLOW_ID)
triggers = defn["properties"]["definition"]["triggers"]
manual = next(iter(triggers.values())) # usually the only trigger on HTTP flows
request_schema = manual.get("inputs", {}).get("schema")
print("Expected body schema:", request_schema)
# Response schemas live on Response action(s) in the actions block
for name, act in defn["properties"]["definition"]["actions"].items():
if act.get("type") == "Response":
print(f"Response {name}:", act.get("inputs", {}).get("schema"))
# Trigger with a test payload
result = mcp("trigger_live_flow",
environmentName=ENV,
flowName=FLOW_ID,
body={"name": "Test User", "value": 42})
print(f"Status: {result['responseStatus']}, Body: {result.get('responseBody')}")
print(f"Kind: {result['triggerKind']}, via: {result['invocation']}, run: {result.get('runName')}")
if result.get("warning"):
print(result["warning"]) # required trigger inputs you left out
trigger_live_flowは、AAD 認証されたトリガーを自動的に処理します。Requestトリガー(HTTP 要求、Button、PowerApps)と、スケジュールされた(Recurrence)フローに対応しており、スケジュールされたフローは即座に実行されます。スケジュールされたトリガーは入力を受け取らないため、bodyは指定しません(指定すると拒否されます)。自動化されたコネクタートリガーは、ソースイベントからのみ起動します。Power Automate は、トリガーの
required入力を強制しません。入力を省略しても実行は開始され、その入力は null になり、結果には不足しているキーを示すwarningが含まれます。それが問題になる場合は、実行をキャンセルし、すべての入力を含む body を指定して再度呼び出してください。
runNameが返されるのは、Button と PowerApps の実行のみです。HTTP トリガーの場合は、get_live_flow_runsで実行を探してください。ブラウザー拡張機能のキーを使用する場合、Button と PowerApps のトリガーは空の body でのみ実行されます。ツールはその旨を伝え、回避方法を示します。過去の実行を再送信する、フロー内で
coalesce(triggerBody()?['x'], 'value')を使って入力に既定値を設定する、または標準の API キーを使用する方法があります。
クイックリファレンス:診断の判断ツリー
| 症状 | 最初のツール | 次に必ず呼び出すツール | 確認する内容 |
|---|---|---|---|
| フローが Failed と表示される | get_live_flow_run_error | 失敗したアクションに対して get_live_flow_run_action_outputs | outputs 内の HTTP ステータスとレスポンス body |
エラーコードが汎用的(ActionFailed、NotSpecified) | — | get_live_flow_run_action_outputs | outputs.body に実際のエラーメッセージ、スタックトレース、または API エラーが含まれている |
| HTTP アクションが 500 を返す | — | get_live_flow_run_action_outputs | サーバーエラーの詳細を示す outputs.statusCode と outputs.body |
| 式のクラッシュ | — | 前のアクションに対して get_live_flow_run_action_outputs | 出力 body 内の null フィールド、または型が誤っているフィールド |
| フローが開始されない | get_live_flow | — | properties.state = "Started" であることを確認 |
| アクションが誤ったデータを返す | get_live_flow_run_action_outputs | — | 実際の出力 body と期待される出力を比較 |
| 修正を適用しても失敗する | 再送信後に get_live_flow_runs | — | 新しい実行の status フィールド |
ルール:エラーコードだけで原因を診断してはいけません。
get_live_flow_run_errorは失敗したアクションを特定します。get_live_flow_run_action_outputsは 実際の原因を明らかにします。必ず両方を呼び出してください。
参照ファイル
- common-errors.md — エラーコード、考えられる原因、修正方法
- debug-workflow.md — 複雑な障害に対応する完全な判断ツリー
関連スキル
power-automate-mcp— 基礎スキル:接続のセットアップ、MCP ヘルパー、ツールの検出power-automate-build— 新しいフローの構築とデプロイ