惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

阮一峰的网络日志
阮一峰的网络日志
博客园 - 司徒正美
D
DataBreaches.Net
宝玉的分享
宝玉的分享
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 【当耐特】
人人都是产品经理
人人都是产品经理
博客园 - Franky
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
IT之家
IT之家
博客园 - 三生石上(FineUI控件)
J
Java Code Geeks
腾讯CDC
博客园_首页
The Cloudflare Blog
S
SegmentFault 最新的问题
C
Check Point Blog
美团技术团队
爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix #94040: [Bug]: nodes approve failed: GatewayClientReq...
mushuiyu886 · 2026-06-27 · via Recent Commits to openclaw:main

@@ -6,7 +6,7 @@ import type { OperatorScope } from "../../gateway/method-scopes.js";

66

import { resolveNodePairApprovalScopes } from "../../infra/node-pairing-authz.js";

77

import { defaultRuntime } from "../../runtime.js";

88

import { formatCliCommand } from "../command-format.js";

9-

import { getNodesTheme, runNodesCommand } from "./cli-utils.js";

9+

import { formatConnectionFlagReminder, getNodesTheme, runNodesCommand } from "./cli-utils.js";

1010

import { parsePairingList } from "./format.js";

1111

import { renderPendingPairingRequestsTable } from "./pairing-render.js";

1212

import {

@@ -44,25 +44,67 @@ function normalizeNodePairApproveScopes(scopes: unknown): OperatorScope[] {

4444

async function resolveApproveScopesForRequest(

4545

opts: NodesRpcOpts,

4646

requestId: string,

47-

): Promise<OperatorScope[]> {

47+

): Promise<{ scopes: OperatorScope[] }> {

48+

let pending: PendingRequest[];

4849

try {

4950

const result = await callNodePairApprovalGatewayCli(

5051

"node.pair.list",

5152

opts,

5253

{},

5354

{ scopes: DEFAULT_NODE_PAIR_APPROVE_SCOPES },

5455

);

55-

const { pending } = parsePairingList(result);

56-

const request = pending.find((candidate: PendingRequest) => candidate.requestId === requestId);

57-

const scopes = normalizeNodePairApproveScopes(request?.requiredApproveScopes);

58-

if (scopes.length > DEFAULT_NODE_PAIR_APPROVE_SCOPES.length) {

59-

return scopes;

60-

}

61-

// Older pending requests only list requested commands; derive approval scopes from them.

62-

return resolveNodePairApprovalScopes(request?.commands) as OperatorScope[];

56+

pending = parsePairingList(result).pending;

6357

} catch {

64-

return [...DEFAULT_NODE_PAIR_APPROVE_SCOPES];

58+

return { scopes: [...DEFAULT_NODE_PAIR_APPROVE_SCOPES] };

59+

}

60+

const pendingRequestIds = pending

61+

.map((request) => request.requestId)

62+

.filter((id): id is string => typeof id === "string" && id.length > 0);

63+

const request = pending.find((candidate) => candidate.requestId === requestId);

64+

if (!request) {

65+

throw new Error(buildUnknownNodePairRequestIdMessage(requestId, opts, pendingRequestIds));

6566

}

67+

const declaredScopes = normalizeNodePairApproveScopes(request.requiredApproveScopes);

68+

if (declaredScopes.length > DEFAULT_NODE_PAIR_APPROVE_SCOPES.length) {

69+

return { scopes: declaredScopes };

70+

}

71+

// Older pending requests only list requested commands; derive approval scopes from them.

72+

return {

73+

scopes: resolveNodePairApprovalScopes(request.commands) as OperatorScope[],

74+

};

75+

}

76+77+

function isUnknownNodePairRequestIdError(

78+

error: unknown,

79+

): error is Error & { gatewayCode: "INVALID_REQUEST" } {

80+

const requestError = error as (Error & { gatewayCode?: unknown }) | undefined;

81+

return (

82+

requestError instanceof Error &&

83+

requestError.name === "GatewayClientRequestError" &&

84+

requestError.gatewayCode === "INVALID_REQUEST" &&

85+

requestError.message === "unknown requestId"

86+

);

87+

}

88+89+

function buildUnknownNodePairRequestIdMessage(

90+

requestId: string,

91+

opts: NodesRpcOpts,

92+

pendingRequestIds?: string[],

93+

): string {

94+

const lines = [`Unknown node pairing requestId: ${requestId}`];

95+

if (pendingRequestIds !== undefined) {

96+

if (pendingRequestIds.length > 0) {

97+

lines.push(`Pending requestIds: ${pendingRequestIds.join(", ")}`);

98+

} else {

99+

lines.push("No pending node pairing requests are currently visible.");

100+

}

101+

}

102+

lines.push(`Run ${formatCliCommand("openclaw nodes pending")} to inspect current requests.`);

103+

const connectionReminder = formatConnectionFlagReminder(opts);

104+

if (connectionReminder) {

105+

lines.push(connectionReminder);

106+

}

107+

return lines.join("\n");

66108

}

6710968110

/** Register node pairing management commands. */

@@ -106,17 +148,28 @@ export function registerNodesPairingCommands(nodes: Command) {

106148

.argument("<requestId>", "Pending request id")

107149

.action(async (requestId: string, opts: NodesRpcOpts) => {

108150

await runNodesCommand("approve", async () => {

109-

const scopes = await resolveApproveScopesForRequest(opts, requestId);

110-

const result = await callNodePairApprovalGatewayCli(

111-

"node.pair.approve",

112-

opts,

113-

{

114-

requestId,

115-

},

116-

{

117-

scopes,

118-

},

119-

);

151+

const { scopes } = await resolveApproveScopesForRequest(opts, requestId);

152+

let result: unknown;

153+

try {

154+

result = await callNodePairApprovalGatewayCli(

155+

"node.pair.approve",

156+

opts,

157+

{

158+

requestId,

159+

},

160+

{

161+

scopes,

162+

},

163+

);

164+

} catch (error) {

165+

if (!isUnknownNodePairRequestIdError(error)) {

166+

throw error;

167+

}

168+

// Reuse the gateway error so generic formatting does not append its raw cause.

169+

error.name = "Error";

170+

error.message = buildUnknownNodePairRequestIdMessage(requestId, opts);

171+

throw error;

172+

}

120173

defaultRuntime.writeJson(result);

121174

});

122175

}),