From 7460566cd32f27c9eb90934b468d20be0bc47adb Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Tue, 16 Dec 2025 16:12:34 -0600 Subject: [PATCH 1/2] fix: handle runAction errors gracefully --- genkit-tools/common/src/manager/manager.ts | 61 ++++++++++++++++++++-- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/genkit-tools/common/src/manager/manager.ts b/genkit-tools/common/src/manager/manager.ts index 2a78247bbb..45e26d53d1 100644 --- a/genkit-tools/common/src/manager/manager.ts +++ b/genkit-tools/common/src/manager/manager.ts @@ -228,7 +228,9 @@ export class RuntimeManager { responseType: 'stream', } ) - .catch(this.httpErrorHandler); + .catch((err) => + this.handleStreamError(err, `Error running action key='${input.key}'.`) + ); let genkitVersion: string; if (response.headers['x-genkit-version']) { genkitVersion = response.headers['x-genkit-version']; @@ -302,7 +304,7 @@ export class RuntimeManager { responseType: 'stream', // Use stream to get early headers }) .catch((err) => - this.httpErrorHandler(err, `Error running action key='${input.key}'.`) + this.handleStreamError(err, `Error running action key='${input.key}'.`) ); const traceId = response.headers['x-genkit-trace-id']; @@ -479,7 +481,7 @@ export class RuntimeManager { responseType: 'stream', }) .catch((err) => - this.httpErrorHandler( + this.handleStreamError( err, `Error streaming trace for traceId='${traceId}'` ) @@ -735,7 +737,7 @@ export class RuntimeManager { /** * Handles an HTTP error. */ - private httpErrorHandler(error: AxiosError, message?: string): any { + private httpErrorHandler(error: AxiosError, message?: string): never { const newError = new GenkitToolsError(message || 'Internal Error'); if (error.response) { @@ -753,6 +755,57 @@ export class RuntimeManager { }); } + /** + * Handles a stream error by reading the stream and then calling httpErrorHandler. + */ + private async handleStreamError( + error: AxiosError, + message: string + ): Promise { + if ( + error.response && + error.config?.responseType === 'stream' && + (error.response.data as any).on + ) { + try { + const body = await this.streamToString(error.response.data); + try { + error.response.data = JSON.parse(body); + } catch (e) { + error.response.data = { + message: body || 'Unknown error', + }; + } + } catch (e) { + // If stream reading fails, we must replace the stream object with a safe error object + // to prevent circular structure errors during JSON serialization. + error.response.data = { + message: 'Failed to read error response stream', + details: String(e), + }; + } + } + this.httpErrorHandler(error, message); + } + + /** + * Helper to convert a stream to string. + */ + private streamToString(stream: any): Promise { + return new Promise((resolve, reject) => { + let buffer = ''; + stream.on('data', (chunk: Buffer) => { + buffer += chunk.toString(); + }); + stream.on('end', () => { + resolve(buffer); + }); + stream.on('error', (err: Error) => { + reject(err); + }); + }); + } + /** * Performs health checks on all runtimes. */ From ab2aeac0154f458afad8d2af6acf3dc3a1387e22 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Tue, 16 Dec 2025 19:35:00 -0600 Subject: [PATCH 2/2] format --- genkit-tools/common/src/manager/manager.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/genkit-tools/common/src/manager/manager.ts b/genkit-tools/common/src/manager/manager.ts index 45e26d53d1..e5463c838a 100644 --- a/genkit-tools/common/src/manager/manager.ts +++ b/genkit-tools/common/src/manager/manager.ts @@ -229,7 +229,10 @@ export class RuntimeManager { } ) .catch((err) => - this.handleStreamError(err, `Error running action key='${input.key}'.`) + this.handleStreamError( + err, + `Error running action key='${input.key}'.` + ) ); let genkitVersion: string; if (response.headers['x-genkit-version']) { @@ -304,7 +307,10 @@ export class RuntimeManager { responseType: 'stream', // Use stream to get early headers }) .catch((err) => - this.handleStreamError(err, `Error running action key='${input.key}'.`) + this.handleStreamError( + err, + `Error running action key='${input.key}'.` + ) ); const traceId = response.headers['x-genkit-trace-id'];