From b19aae4013c6a16f16bb5534b06e21335f2ed3a7 Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Thu, 15 Jan 2026 14:34:14 -0500 Subject: [PATCH 1/3] feat: Improve error messages with actionable guidance - Extract and display error details from API response body - Add specific guidance for common errors: - Nested archives: suggest using .gitattributes export-ignore - Size limits: suggest excluding large files - Rate limits: suggest waiting before retry - Handle 401, 413, 429, and 500 status codes with helpful messages --- dist/index.js | 58 ++++++++++++++++++++++++++++++++++++++++++++------- src/index.ts | 56 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/dist/index.js b/dist/index.js index c24d4d0..73ea602 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32575,21 +32575,65 @@ async function run() { } } catch (error) { + let errorMessage = 'An unknown error occurred'; + let helpText = ''; if (error.response) { const status = error.response.status; + // Try to extract error details from response body + let apiMessage = ''; + try { + const responseData = error.response.data; + if (typeof responseData === 'string') { + apiMessage = responseData; + } + else if (responseData?.message) { + apiMessage = responseData.message; + } + else if (responseData?.error) { + apiMessage = responseData.error; + } + } + catch { + // Ignore parsing errors + } if (status === 401) { - core.error('Invalid API key. Get your key at https://dashboard.supermodeltools.com'); + errorMessage = 'Invalid API key'; + helpText = 'Get your key at https://dashboard.supermodeltools.com'; + } + else if (status === 500) { + errorMessage = apiMessage || 'Internal server error'; + // Check for common issues and provide guidance + if (apiMessage.includes('Nested archives')) { + helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + + 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + + 'Example: tests/fixtures/*.zip export-ignore'; + } + else if (apiMessage.includes('exceeds maximum')) { + helpText = 'Your repository or a file within it exceeds size limits. ' + + 'Consider excluding large files using .gitattributes with "export-ignore".'; + } + } + else if (status === 413) { + errorMessage = 'Repository archive too large'; + helpText = 'Reduce archive size by excluding large files in .gitattributes'; + } + else if (status === 429) { + errorMessage = 'Rate limit exceeded'; + helpText = 'Please wait before retrying'; } else { - core.error(`API error (${status})`); + errorMessage = apiMessage || `API error (${status})`; + } + core.error(`Error: ${errorMessage}`); + if (helpText) { + core.error(`Help: ${helpText}`); } } - if (error instanceof Error) { - core.setFailed(error.message); - } - else { - core.setFailed('An unknown error occurred'); + else if (error instanceof Error) { + errorMessage = error.message; + core.error(`Error: ${errorMessage}`); } + core.setFailed(errorMessage); } } run(); diff --git a/src/index.ts b/src/index.ts index d9ce640..3e71c30 100644 --- a/src/index.ts +++ b/src/index.ts @@ -120,20 +120,62 @@ async function run(): Promise { } } catch (error: any) { + let errorMessage = 'An unknown error occurred'; + let helpText = ''; + if (error.response) { const status = error.response.status; + + // Try to extract error details from response body + let apiMessage = ''; + try { + const responseData = error.response.data; + if (typeof responseData === 'string') { + apiMessage = responseData; + } else if (responseData?.message) { + apiMessage = responseData.message; + } else if (responseData?.error) { + apiMessage = responseData.error; + } + } catch { + // Ignore parsing errors + } + if (status === 401) { - core.error('Invalid API key. Get your key at https://dashboard.supermodeltools.com'); + errorMessage = 'Invalid API key'; + helpText = 'Get your key at https://dashboard.supermodeltools.com'; + } else if (status === 500) { + errorMessage = apiMessage || 'Internal server error'; + + // Check for common issues and provide guidance + if (apiMessage.includes('Nested archives')) { + helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + + 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + + 'Example: tests/fixtures/*.zip export-ignore'; + } else if (apiMessage.includes('exceeds maximum')) { + helpText = 'Your repository or a file within it exceeds size limits. ' + + 'Consider excluding large files using .gitattributes with "export-ignore".'; + } + } else if (status === 413) { + errorMessage = 'Repository archive too large'; + helpText = 'Reduce archive size by excluding large files in .gitattributes'; + } else if (status === 429) { + errorMessage = 'Rate limit exceeded'; + helpText = 'Please wait before retrying'; } else { - core.error(`API error (${status})`); + errorMessage = apiMessage || `API error (${status})`; } - } - if (error instanceof Error) { - core.setFailed(error.message); - } else { - core.setFailed('An unknown error occurred'); + core.error(`Error: ${errorMessage}`); + if (helpText) { + core.error(`Help: ${helpText}`); + } + } else if (error instanceof Error) { + errorMessage = error.message; + core.error(`Error: ${errorMessage}`); } + + core.setFailed(errorMessage); } } From 5eecf6ff888d360f1748dc11037de0283dd4e31c Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Thu, 15 Jan 2026 16:21:02 -0500 Subject: [PATCH 2/3] feat: Add comprehensive error debug logging --- dist/index.js | 126 ++++++++++++++++++++++++++++++-------------------- src/index.ts | 120 +++++++++++++++++++++++++++++------------------ 2 files changed, 149 insertions(+), 97 deletions(-) diff --git a/dist/index.js b/dist/index.js index 73ea602..76f327b 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32575,63 +32575,87 @@ async function run() { } } catch (error) { + // Log full error details for debugging + core.info('--- Error Debug Info ---'); + core.info(`Error type: ${error?.constructor?.name}`); + core.info(`Error message: ${error?.message}`); + core.info(`Error name: ${error?.name}`); + // Check various error structures used by different HTTP clients + if (error?.response) { + core.info(`Response status: ${error.response.status}`); + core.info(`Response statusText: ${error.response.statusText}`); + core.info(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); + core.info(`Response headers: ${JSON.stringify(error.response.headers, null, 2)}`); + } + if (error?.body) { + core.info(`Error body: ${JSON.stringify(error.body, null, 2)}`); + } + if (error?.status) { + core.info(`Error status: ${error.status}`); + } + if (error?.statusCode) { + core.info(`Error statusCode: ${error.statusCode}`); + } + if (error?.cause) { + core.info(`Error cause: ${JSON.stringify(error.cause, null, 2)}`); + } + core.info('--- End Debug Info ---'); let errorMessage = 'An unknown error occurred'; let helpText = ''; - if (error.response) { - const status = error.response.status; - // Try to extract error details from response body - let apiMessage = ''; - try { - const responseData = error.response.data; - if (typeof responseData === 'string') { - apiMessage = responseData; - } - else if (responseData?.message) { - apiMessage = responseData.message; - } - else if (responseData?.error) { - apiMessage = responseData.error; - } - } - catch { - // Ignore parsing errors - } - if (status === 401) { - errorMessage = 'Invalid API key'; - helpText = 'Get your key at https://dashboard.supermodeltools.com'; - } - else if (status === 500) { - errorMessage = apiMessage || 'Internal server error'; - // Check for common issues and provide guidance - if (apiMessage.includes('Nested archives')) { - helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + - 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + - 'Example: tests/fixtures/*.zip export-ignore'; - } - else if (apiMessage.includes('exceeds maximum')) { - helpText = 'Your repository or a file within it exceeds size limits. ' + - 'Consider excluding large files using .gitattributes with "export-ignore".'; - } - } - else if (status === 413) { - errorMessage = 'Repository archive too large'; - helpText = 'Reduce archive size by excluding large files in .gitattributes'; - } - else if (status === 429) { - errorMessage = 'Rate limit exceeded'; - helpText = 'Please wait before retrying'; + // Try multiple error structures + const status = error?.response?.status || error?.status || error?.statusCode; + let apiMessage = ''; + // Try to extract message from various locations + try { + apiMessage = + error?.response?.data?.message || + error?.response?.data?.error || + error?.response?.data || + error?.body?.message || + error?.body?.error || + error?.message || + ''; + if (typeof apiMessage !== 'string') { + apiMessage = JSON.stringify(apiMessage); } - else { - errorMessage = apiMessage || `API error (${status})`; + } + catch { + // Ignore parsing errors + } + if (status === 401) { + errorMessage = 'Invalid API key'; + helpText = 'Get your key at https://dashboard.supermodeltools.com'; + } + else if (status === 500) { + errorMessage = apiMessage || 'Internal server error'; + // Check for common issues and provide guidance + if (apiMessage.includes('Nested archives')) { + helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + + 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + + 'Example: tests/fixtures/*.zip export-ignore'; } - core.error(`Error: ${errorMessage}`); - if (helpText) { - core.error(`Help: ${helpText}`); + else if (apiMessage.includes('exceeds maximum')) { + helpText = 'Your repository or a file within it exceeds size limits. ' + + 'Consider excluding large files using .gitattributes with "export-ignore".'; } } - else if (error instanceof Error) { - errorMessage = error.message; - core.error(`Error: ${errorMessage}`); + else if (status === 413) { + errorMessage = 'Repository archive too large'; + helpText = 'Reduce archive size by excluding large files in .gitattributes'; + } + else if (status === 429) { + errorMessage = 'Rate limit exceeded'; + helpText = 'Please wait before retrying'; + } + else if (status) { + errorMessage = apiMessage || `API error (${status})`; + } + else { + errorMessage = apiMessage || error?.message || 'An unknown error occurred'; + } + core.error(`Error: ${errorMessage}`); + if (helpText) { + core.error(`Help: ${helpText}`); } core.setFailed(errorMessage); } diff --git a/src/index.ts b/src/index.ts index 3e71c30..bc1d9bb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -120,59 +120,87 @@ async function run(): Promise { } } catch (error: any) { + // Log full error details for debugging + core.info('--- Error Debug Info ---'); + core.info(`Error type: ${error?.constructor?.name}`); + core.info(`Error message: ${error?.message}`); + core.info(`Error name: ${error?.name}`); + + // Check various error structures used by different HTTP clients + if (error?.response) { + core.info(`Response status: ${error.response.status}`); + core.info(`Response statusText: ${error.response.statusText}`); + core.info(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); + core.info(`Response headers: ${JSON.stringify(error.response.headers, null, 2)}`); + } + if (error?.body) { + core.info(`Error body: ${JSON.stringify(error.body, null, 2)}`); + } + if (error?.status) { + core.info(`Error status: ${error.status}`); + } + if (error?.statusCode) { + core.info(`Error statusCode: ${error.statusCode}`); + } + if (error?.cause) { + core.info(`Error cause: ${JSON.stringify(error.cause, null, 2)}`); + } + core.info('--- End Debug Info ---'); + let errorMessage = 'An unknown error occurred'; let helpText = ''; - if (error.response) { - const status = error.response.status; - - // Try to extract error details from response body - let apiMessage = ''; - try { - const responseData = error.response.data; - if (typeof responseData === 'string') { - apiMessage = responseData; - } else if (responseData?.message) { - apiMessage = responseData.message; - } else if (responseData?.error) { - apiMessage = responseData.error; - } - } catch { - // Ignore parsing errors + // Try multiple error structures + const status = error?.response?.status || error?.status || error?.statusCode; + let apiMessage = ''; + + // Try to extract message from various locations + try { + apiMessage = + error?.response?.data?.message || + error?.response?.data?.error || + error?.response?.data || + error?.body?.message || + error?.body?.error || + error?.message || + ''; + if (typeof apiMessage !== 'string') { + apiMessage = JSON.stringify(apiMessage); } + } catch { + // Ignore parsing errors + } - if (status === 401) { - errorMessage = 'Invalid API key'; - helpText = 'Get your key at https://dashboard.supermodeltools.com'; - } else if (status === 500) { - errorMessage = apiMessage || 'Internal server error'; - - // Check for common issues and provide guidance - if (apiMessage.includes('Nested archives')) { - helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + - 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + - 'Example: tests/fixtures/*.zip export-ignore'; - } else if (apiMessage.includes('exceeds maximum')) { - helpText = 'Your repository or a file within it exceeds size limits. ' + - 'Consider excluding large files using .gitattributes with "export-ignore".'; - } - } else if (status === 413) { - errorMessage = 'Repository archive too large'; - helpText = 'Reduce archive size by excluding large files in .gitattributes'; - } else if (status === 429) { - errorMessage = 'Rate limit exceeded'; - helpText = 'Please wait before retrying'; - } else { - errorMessage = apiMessage || `API error (${status})`; + if (status === 401) { + errorMessage = 'Invalid API key'; + helpText = 'Get your key at https://dashboard.supermodeltools.com'; + } else if (status === 500) { + errorMessage = apiMessage || 'Internal server error'; + + // Check for common issues and provide guidance + if (apiMessage.includes('Nested archives')) { + helpText = 'Your repository contains nested archive files (.zip, .tar, etc.). ' + + 'Add them to .gitattributes with "export-ignore" to exclude from analysis. ' + + 'Example: tests/fixtures/*.zip export-ignore'; + } else if (apiMessage.includes('exceeds maximum')) { + helpText = 'Your repository or a file within it exceeds size limits. ' + + 'Consider excluding large files using .gitattributes with "export-ignore".'; } + } else if (status === 413) { + errorMessage = 'Repository archive too large'; + helpText = 'Reduce archive size by excluding large files in .gitattributes'; + } else if (status === 429) { + errorMessage = 'Rate limit exceeded'; + helpText = 'Please wait before retrying'; + } else if (status) { + errorMessage = apiMessage || `API error (${status})`; + } else { + errorMessage = apiMessage || error?.message || 'An unknown error occurred'; + } - core.error(`Error: ${errorMessage}`); - if (helpText) { - core.error(`Help: ${helpText}`); - } - } else if (error instanceof Error) { - errorMessage = error.message; - core.error(`Error: ${errorMessage}`); + core.error(`Error: ${errorMessage}`); + if (helpText) { + core.error(`Help: ${helpText}`); } core.setFailed(errorMessage); From 97e38c18f92cbcff547da99d0c2bedc9b7c7163e Mon Sep 17 00:00:00 2001 From: jonathanpopham Date: Thu, 15 Jan 2026 16:29:59 -0500 Subject: [PATCH 3/3] fix: Secure error logging with redaction and safe serialization - Add safeSerialize helper to handle circular refs, BigInt, and truncate large values - Add redactSensitive helper to redact Authorization, Cookie, token fields - Use core.debug for sensitive details (headers, cause) - Use core.info for safe error summaries (status, message, data) - Wrap all serialization in try/catch to prevent throws - Add SENSITIVE_KEYS set for consistent redaction --- dist/index.js | 119 +++++++++++++++++++++++++++++++++++++--------- src/index.ts | 129 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 203 insertions(+), 45 deletions(-) diff --git a/dist/index.js b/dist/index.js index 76f327b..d1b9d7c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -32488,6 +32488,74 @@ const fs = __importStar(__nccwpck_require__(1943)); const path = __importStar(__nccwpck_require__(6928)); const sdk_1 = __nccwpck_require__(6381); const dead_code_1 = __nccwpck_require__(1655); +/** Fields that should be redacted from logs */ +const SENSITIVE_KEYS = new Set([ + 'authorization', + 'cookie', + 'set-cookie', + 'access_token', + 'refresh_token', + 'api_key', + 'apikey', + 'password', + 'secret', + 'token', + 'x-api-key', +]); +const MAX_VALUE_LENGTH = 1000; +/** + * Safely serialize a value for logging, handling circular refs, BigInt, and large values. + * Redacts sensitive fields. + */ +function safeSerialize(value, maxLength = MAX_VALUE_LENGTH) { + try { + const seen = new WeakSet(); + const serialized = JSON.stringify(value, (key, val) => { + // Redact sensitive keys + if (key && SENSITIVE_KEYS.has(key.toLowerCase())) { + return '[REDACTED]'; + } + // Handle BigInt + if (typeof val === 'bigint') { + return val.toString(); + } + // Handle circular references + if (typeof val === 'object' && val !== null) { + if (seen.has(val)) { + return '[Circular]'; + } + seen.add(val); + } + return val; + }, 2); + // Truncate if too long + if (serialized && serialized.length > maxLength) { + return serialized.slice(0, maxLength) + '... [truncated]'; + } + return serialized ?? '[undefined]'; + } + catch { + return '[unserializable]'; + } +} +/** + * Redact sensitive fields from an object (shallow copy). + */ +function redactSensitive(obj) { + if (!obj || typeof obj !== 'object') { + return null; + } + const result = {}; + for (const [key, value] of Object.entries(obj)) { + if (SENSITIVE_KEYS.has(key.toLowerCase())) { + result[key] = '[REDACTED]'; + } + else { + result[key] = value; + } + } + return result; +} async function createZipArchive(workspacePath) { const zipPath = path.join(workspacePath, '.dead-code-hunter-repo.zip'); core.info('Creating zip archive...'); @@ -32575,29 +32643,36 @@ async function run() { } } catch (error) { - // Log full error details for debugging + // Log error details for debugging (using debug level for potentially sensitive data) core.info('--- Error Debug Info ---'); - core.info(`Error type: ${error?.constructor?.name}`); - core.info(`Error message: ${error?.message}`); - core.info(`Error name: ${error?.name}`); + core.info(`Error type: ${error?.constructor?.name ?? 'unknown'}`); + core.info(`Error message: ${error?.message ?? 'no message'}`); + core.info(`Error name: ${error?.name ?? 'no name'}`); // Check various error structures used by different HTTP clients - if (error?.response) { - core.info(`Response status: ${error.response.status}`); - core.info(`Response statusText: ${error.response.statusText}`); - core.info(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); - core.info(`Response headers: ${JSON.stringify(error.response.headers, null, 2)}`); - } - if (error?.body) { - core.info(`Error body: ${JSON.stringify(error.body, null, 2)}`); - } - if (error?.status) { - core.info(`Error status: ${error.status}`); - } - if (error?.statusCode) { - core.info(`Error statusCode: ${error.statusCode}`); + // Use core.debug for detailed/sensitive info, core.info for safe summaries + try { + if (error?.response) { + core.info(`Response status: ${error.response.status ?? 'unknown'}`); + core.info(`Response statusText: ${error.response.statusText ?? 'unknown'}`); + core.info(`Response data: ${safeSerialize(error.response.data)}`); + // Headers may contain sensitive values - use debug level + core.debug(`Response headers: ${safeSerialize(redactSensitive(error.response.headers))}`); + } + if (error?.body) { + core.info(`Error body: ${safeSerialize(error.body)}`); + } + if (error?.status) { + core.info(`Error status: ${error.status}`); + } + if (error?.statusCode) { + core.info(`Error statusCode: ${error.statusCode}`); + } + if (error?.cause) { + core.debug(`Error cause: ${safeSerialize(error.cause)}`); + } } - if (error?.cause) { - core.info(`Error cause: ${JSON.stringify(error.cause, null, 2)}`); + catch { + core.debug('Failed to serialize some error properties'); } core.info('--- End Debug Info ---'); let errorMessage = 'An unknown error occurred'; @@ -32616,11 +32691,11 @@ async function run() { error?.message || ''; if (typeof apiMessage !== 'string') { - apiMessage = JSON.stringify(apiMessage); + apiMessage = safeSerialize(apiMessage, 500); } } catch { - // Ignore parsing errors + apiMessage = ''; } if (status === 401) { errorMessage = 'Invalid API key'; diff --git a/src/index.ts b/src/index.ts index bc1d9bb..d11d522 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,83 @@ import * as path from 'path'; import { Configuration, DefaultApi } from '@supermodeltools/sdk'; import { findDeadCode, formatPrComment } from './dead-code'; +/** Fields that should be redacted from logs */ +const SENSITIVE_KEYS = new Set([ + 'authorization', + 'cookie', + 'set-cookie', + 'access_token', + 'refresh_token', + 'api_key', + 'apikey', + 'password', + 'secret', + 'token', + 'x-api-key', +]); + +const MAX_VALUE_LENGTH = 1000; + +/** + * Safely serialize a value for logging, handling circular refs, BigInt, and large values. + * Redacts sensitive fields. + */ +function safeSerialize(value: unknown, maxLength = MAX_VALUE_LENGTH): string { + try { + const seen = new WeakSet(); + + const serialized = JSON.stringify(value, (key, val) => { + // Redact sensitive keys + if (key && SENSITIVE_KEYS.has(key.toLowerCase())) { + return '[REDACTED]'; + } + + // Handle BigInt + if (typeof val === 'bigint') { + return val.toString(); + } + + // Handle circular references + if (typeof val === 'object' && val !== null) { + if (seen.has(val)) { + return '[Circular]'; + } + seen.add(val); + } + + return val; + }, 2); + + // Truncate if too long + if (serialized && serialized.length > maxLength) { + return serialized.slice(0, maxLength) + '... [truncated]'; + } + + return serialized ?? '[undefined]'; + } catch { + return '[unserializable]'; + } +} + +/** + * Redact sensitive fields from an object (shallow copy). + */ +function redactSensitive(obj: Record | null | undefined): Record | null { + if (!obj || typeof obj !== 'object') { + return null; + } + + const result: Record = {}; + for (const [key, value] of Object.entries(obj)) { + if (SENSITIVE_KEYS.has(key.toLowerCase())) { + result[key] = '[REDACTED]'; + } else { + result[key] = value; + } + } + return result; +} + async function createZipArchive(workspacePath: string): Promise { const zipPath = path.join(workspacePath, '.dead-code-hunter-repo.zip'); @@ -120,30 +197,36 @@ async function run(): Promise { } } catch (error: any) { - // Log full error details for debugging + // Log error details for debugging (using debug level for potentially sensitive data) core.info('--- Error Debug Info ---'); - core.info(`Error type: ${error?.constructor?.name}`); - core.info(`Error message: ${error?.message}`); - core.info(`Error name: ${error?.name}`); + core.info(`Error type: ${error?.constructor?.name ?? 'unknown'}`); + core.info(`Error message: ${error?.message ?? 'no message'}`); + core.info(`Error name: ${error?.name ?? 'no name'}`); // Check various error structures used by different HTTP clients - if (error?.response) { - core.info(`Response status: ${error.response.status}`); - core.info(`Response statusText: ${error.response.statusText}`); - core.info(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); - core.info(`Response headers: ${JSON.stringify(error.response.headers, null, 2)}`); - } - if (error?.body) { - core.info(`Error body: ${JSON.stringify(error.body, null, 2)}`); - } - if (error?.status) { - core.info(`Error status: ${error.status}`); - } - if (error?.statusCode) { - core.info(`Error statusCode: ${error.statusCode}`); - } - if (error?.cause) { - core.info(`Error cause: ${JSON.stringify(error.cause, null, 2)}`); + // Use core.debug for detailed/sensitive info, core.info for safe summaries + try { + if (error?.response) { + core.info(`Response status: ${error.response.status ?? 'unknown'}`); + core.info(`Response statusText: ${error.response.statusText ?? 'unknown'}`); + core.info(`Response data: ${safeSerialize(error.response.data)}`); + // Headers may contain sensitive values - use debug level + core.debug(`Response headers: ${safeSerialize(redactSensitive(error.response.headers))}`); + } + if (error?.body) { + core.info(`Error body: ${safeSerialize(error.body)}`); + } + if (error?.status) { + core.info(`Error status: ${error.status}`); + } + if (error?.statusCode) { + core.info(`Error statusCode: ${error.statusCode}`); + } + if (error?.cause) { + core.debug(`Error cause: ${safeSerialize(error.cause)}`); + } + } catch { + core.debug('Failed to serialize some error properties'); } core.info('--- End Debug Info ---'); @@ -165,10 +248,10 @@ async function run(): Promise { error?.message || ''; if (typeof apiMessage !== 'string') { - apiMessage = JSON.stringify(apiMessage); + apiMessage = safeSerialize(apiMessage, 500); } } catch { - // Ignore parsing errors + apiMessage = ''; } if (status === 401) {