@@ -34,6 +34,10 @@ type BridgeInternals = {
3434 event : string ;
3535 payload ?: Record < string , unknown > ;
3636 } ) => Promise < void > ;
37+ dispatchGatewayEvent : ( event : {
38+ event : string ;
39+ payload ?: Record < string , unknown > ;
40+ } ) => Promise < void > ;
3741 handleSessionMessageEvent : ( payload : {
3842 sessionKey : string ;
3943 senderIsOwner ?: boolean ;
@@ -304,6 +308,52 @@ describe("OpenClawChannelBridge — pendingClaudePermissions / pendingApprovals
304308 }
305309 } ) ;
306310
311+ test ( "a rejected gateway event still emits exactly one diagnostic record with verbose off" , async ( ) => {
312+ const bridge = makeBridge ( false ) ;
313+ const writes : string [ ] = [ ] ;
314+ const writeSpy = vi
315+ . spyOn ( process . stderr , "write" )
316+ . mockImplementation ( ( chunk : string | Uint8Array ) : boolean => {
317+ writes . push ( String ( chunk ) ) ;
318+ return true ;
319+ } ) ;
320+ vi . spyOn ( bridge , "handleGatewayEvent" ) . mockRejectedValue ( new Error ( "handler boom" ) ) ;
321+ try {
322+ await bridge . dispatchGatewayEvent ( { event : "exec.approval.requested" , payload : { } } ) ;
323+
324+ expect ( writes ) . toHaveLength ( 1 ) ;
325+ expect ( writes [ 0 ] ) . toBe ( "openclaw mcp: gateway event exec.approval.requested failed\n" ) ;
326+ expect ( writes [ 0 ] ) . not . toContain ( "handler boom" ) ;
327+ } finally {
328+ writeSpy . mockRestore ( ) ;
329+ await bridge . close ( ) ;
330+ }
331+ } ) ;
332+
333+ test ( "a rejected gateway event includes error detail with verbose on" , async ( ) => {
334+ const bridge = makeBridge ( true ) ;
335+ const writes : string [ ] = [ ] ;
336+ const writeSpy = vi
337+ . spyOn ( process . stderr , "write" )
338+ . mockImplementation ( ( chunk : string | Uint8Array ) : boolean => {
339+ writes . push ( String ( chunk ) ) ;
340+ return true ;
341+ } ) ;
342+ vi . spyOn ( bridge , "handleGatewayEvent" ) . mockRejectedValue ( new Error ( "handler boom" ) ) ;
343+ try {
344+ await bridge . dispatchGatewayEvent ( { event : "exec.approval.requested" , payload : { } } ) ;
345+
346+ expect ( writes ) . toHaveLength ( 2 ) ;
347+ expect ( writes [ 0 ] ) . toBe ( "openclaw mcp: gateway event exec.approval.requested failed\n" ) ;
348+ expect ( writes [ 1 ] ) . toBe (
349+ "openclaw mcp: gateway event exec.approval.requested error: Error: handler boom\n" ,
350+ ) ;
351+ } finally {
352+ writeSpy . mockRestore ( ) ;
353+ await bridge . close ( ) ;
354+ }
355+ } ) ;
356+
307357 test ( "sweeper interval is not started before any pending entry is added" , async ( ) => {
308358 const bridge = makeBridge ( ) ;
309359 try {
0 commit comments