Skip to content

Commit 59f4653

Browse files
added more detailed error output to log when stepzen request fails. (#122)
1 parent 195545b commit 59f4653

File tree

4 files changed

+343
-12
lines changed

4 files changed

+343
-12
lines changed

src/errors/CliError.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,63 @@
11
import { StepZenError } from './StepZenError';
22

3+
/**
4+
* Structured error data extracted from StepZen CLI output
5+
*/
6+
export interface StepZenCliErrorDetails {
7+
/** The original error message */
8+
originalMessage: string;
9+
/** The type of error (authorization, graphql, etc.) */
10+
errorType?: string;
11+
/** Specific error details for GraphQL errors */
12+
graphqlErrors?: string[];
13+
/** Authorization error details */
14+
authError?: string;
15+
}
16+
317
/**
418
* Error class for CLI-related errors
519
* Represents errors that occur when interacting with the StepZen CLI
620
*/
721
export class CliError extends StepZenError {
22+
/** Structured error details extracted from CLI output */
23+
public details?: StepZenCliErrorDetails;
24+
825
/**
926
* Creates a new CLI error
1027
*
1128
* @param message The error message
1229
* @param code A unique code representing the specific error
1330
* @param cause The underlying cause of this error (optional)
31+
* @param details Structured error details extracted from CLI output (optional)
1432
*/
15-
constructor(message: string, code: string = 'CLI_ERROR', cause?: unknown) {
33+
constructor(message: string, code: string = 'CLI_ERROR', cause?: unknown, details?: StepZenCliErrorDetails) {
1634
super(message, code, cause);
1735
this.name = 'CliError';
36+
this.details = details;
1837
}
19-
}
38+
39+
/**
40+
* Creates a string representation of the error including details if available
41+
*/
42+
public toString(): string {
43+
let result = `${this.name}[${this.code}]: ${this.message}`;
44+
45+
if (this.details) {
46+
if (this.details.errorType) {
47+
result += `\nError Type: ${this.details.errorType}`;
48+
}
49+
50+
if (this.details.authError) {
51+
result += `\nAuthorization Error: ${this.details.authError}`;
52+
}
53+
54+
if (this.details.graphqlErrors && this.details.graphqlErrors.length > 0) {
55+
result += `\nGraphQL Errors:\n${this.details.graphqlErrors.map(err => `- ${err}`).join('\n')}`;
56+
}
57+
}
58+
59+
return result;
60+
}
61+
}
62+
63+
// Made with Bob

src/errors/handler.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ export function handleError(error: unknown): StepZenError {
1919
// Step 2: Log the full error with stack trace
2020
logger.error(`${normalizedError.name}[${normalizedError.code}]: ${normalizedError.message}`, normalizedError);
2121

22+
// For CLI errors with details, log the structured information
23+
if (normalizedError instanceof CliError && normalizedError.details) {
24+
if (normalizedError.details.errorType === 'authorization') {
25+
logger.error(`Authorization Error: ${normalizedError.details.authError || 'Access denied'}`);
26+
} else if (normalizedError.details.errorType === 'graphql' && normalizedError.details.graphqlErrors) {
27+
logger.error('GraphQL Errors:');
28+
normalizedError.details.graphqlErrors.forEach(err => {
29+
logger.error(`- ${err}`);
30+
});
31+
}
32+
}
33+
2234
// Step 3: Show VS Code notification with friendly message
2335
showErrorNotification(normalizedError);
2436

@@ -109,10 +121,16 @@ function showErrorNotification(error: StepZenError): void {
109121
// Generate a user-friendly message based on error type
110122
let friendlyMessage = getFriendlyErrorMessage(error);
111123

124+
// For CLI errors with details, add more context to the notification
125+
let detailMessage = error.message;
126+
if (error instanceof CliError && error.details) {
127+
detailMessage = getDetailedErrorMessage(error);
128+
}
129+
112130
// Show notification with "Show Logs" action
113131
vscode.window.showErrorMessage(
114132
friendlyMessage,
115-
{ modal: false, detail: error.message },
133+
{ modal: false, detail: detailMessage },
116134
'Show Logs'
117135
).then(selection => {
118136
if (selection === 'Show Logs') {
@@ -121,6 +139,30 @@ function showErrorNotification(error: StepZenError): void {
121139
});
122140
}
123141

142+
/**
143+
* Get a detailed error message for CLI errors with structured details
144+
*
145+
* @param error The CliError with details
146+
* @returns A detailed error message
147+
*/
148+
function getDetailedErrorMessage(error: CliError): string {
149+
if (!error.details) {
150+
return error.message;
151+
}
152+
153+
let detailMessage = error.message;
154+
155+
// Add specific details based on error type
156+
if (error.details.errorType === 'authorization') {
157+
detailMessage = `Authorization Error: ${error.details.authError || 'Access denied'}`;
158+
}
159+
else if (error.details.errorType === 'graphql' && error.details.graphqlErrors) {
160+
detailMessage = 'GraphQL Errors:\n' + error.details.graphqlErrors.map(err => `- ${err}`).join('\n');
161+
}
162+
163+
return detailMessage;
164+
}
165+
124166
/**
125167
* Get a user-friendly error message based on error type
126168
*
@@ -130,6 +172,15 @@ function showErrorNotification(error: StepZenError): void {
130172
function getFriendlyErrorMessage(error: StepZenError): string {
131173
// Customize message based on error type
132174
if (error instanceof CliError) {
175+
// For CLI errors with details, provide more specific messages
176+
if (error.details) {
177+
if (error.details.errorType === 'authorization') {
178+
return `StepZen Authorization Error: ${error.details.authError || 'Access denied'}`;
179+
}
180+
else if (error.details.errorType === 'graphql') {
181+
return 'StepZen GraphQL Error: The request contains errors';
182+
}
183+
}
133184
return `StepZen CLI Error: ${error.message}`;
134185
}
135186

@@ -143,4 +194,6 @@ function getFriendlyErrorMessage(error: StepZenError): string {
143194

144195
// Default message for base StepZenError
145196
return `StepZen Error: ${error.message}`;
146-
}
197+
}
198+
199+
// Made with Bob

src/services/cli.ts

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as path from 'path';
33
import * as fs from 'fs';
44
import * as os from 'os';
55
import { logger } from './logger';
6-
import { CliError } from '../errors';
6+
import { CliError, StepZenCliErrorDetails } from '../errors';
77
import { TEMP_FILE_PATTERNS, TIMEOUTS } from "../utils/constants";
88

99
/**
@@ -289,6 +289,50 @@ export class StepzenCliService {
289289
}
290290
}
291291

292+
/**
293+
* Parse StepZen CLI error output to extract structured error information
294+
*
295+
* @param stderr The stderr output from the CLI
296+
* @returns Structured error details
297+
*/
298+
private parseCliErrorOutput(stderr: string): StepZenCliErrorDetails {
299+
const details: StepZenCliErrorDetails = {
300+
originalMessage: stderr
301+
};
302+
303+
// Check for authorization errors
304+
if (stderr.includes('Action denied: You are not authorized')) {
305+
details.errorType = 'authorization';
306+
// Extract the specific auth error message
307+
const authMatch = stderr.match(/Action denied: ([^\n]+)/);
308+
if (authMatch && authMatch[1]) {
309+
details.authError = authMatch[1].trim();
310+
}
311+
}
312+
// Check for GraphQL errors
313+
else if (stderr.includes('Errors returned by the GraphQL server:')) {
314+
details.errorType = 'graphql';
315+
details.graphqlErrors = [];
316+
317+
// Extract GraphQL errors - they typically appear after this line
318+
const lines = stderr.split('\n');
319+
let collectingErrors = false;
320+
321+
for (const line of lines) {
322+
const trimmedLine = line.trim();
323+
if (collectingErrors && trimmedLine.startsWith('-')) {
324+
// This is a GraphQL error line, extract the error message
325+
details.graphqlErrors.push(trimmedLine.substring(1).trim());
326+
}
327+
328+
if (trimmedLine.includes('Errors returned by the GraphQL server:')) {
329+
collectingErrors = true;
330+
}
331+
}
332+
}
333+
334+
return details;
335+
}
292336

293337
/**
294338
* Spawn a StepZen CLI process and capture output
@@ -350,15 +394,21 @@ export class StepzenCliService {
350394

351395
proc.on('close', (code) => {
352396
if (code !== 0) {
353-
// Create a more descriptive error with both exit code and stderr content
354-
const errorMsg = stderr.trim()
355-
? `StepZen CLI exited with code ${code}: ${stderr.trim()}`
356-
: `StepZen CLI exited with code ${code}`;
357-
397+
// Parse the stderr to extract structured error information
398+
const errorDetails = this.parseCliErrorOutput(stderr.trim());
399+
400+
// Create a more descriptive error message
401+
let errorMsg = `StepZen CLI exited with code ${code}`;
402+
if (stderr.trim()) {
403+
errorMsg += `: ${stderr.trim()}`;
404+
}
405+
406+
// Create a CliError with the structured error details
358407
reject(new CliError(
359408
errorMsg,
360409
'COMMAND_FAILED',
361-
stderr ? new Error(stderr) : undefined
410+
stderr ? new Error(stderr) : undefined,
411+
errorDetails
362412
));
363413
} else {
364414
logger.debug(`StepZen CLI process completed with exit code 0`);
@@ -367,4 +417,6 @@ export class StepzenCliService {
367417
});
368418
});
369419
}
370-
}
420+
}
421+
422+
// Made with Bob

0 commit comments

Comments
 (0)