fix: Converting circular structure to JSON
This commit is contained in:
1 parent
5a004d9d16
commit
b0ec96d4a6
5 files changed
+91
-12
No files matched your search
@@ -126,7 +126,12 @@ export class QuotaService {
|
|||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
const status = error.response?.status;
|
const status = error.response?.status;
|
||||||
const text = JSON.stringify(error.response?.data || '');
|
let text = '';
|
||||||
|
try {
|
||||||
|
text = JSON.stringify(error.response?.data || '');
|
||||||
|
} catch {
|
||||||
|
text = '[Unable to serialize response data]';
|
||||||
|
}
|
||||||
|
|
||||||
// ✅ Handle 403 Forbidden specifically - return immediately, do not retry
|
// ✅ Handle 403 Forbidden specifically - return immediately, do not retry
|
||||||
if (status === 403) {
|
if (status === 403) {
|
||||||
|
|||||||
+20
-1
@@ -20,9 +20,28 @@ import { ConfigManager } from './ipc/config/manager';
|
|||||||
|
|
||||||
const packetLogPath = path.join(app.getPath('userData'), 'orpc_packets.log');
|
const packetLogPath = path.join(app.getPath('userData'), 'orpc_packets.log');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely stringify an object, handling circular references
|
||||||
|
*/
|
||||||
|
function safeStringifyPacket(obj: unknown): string {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return JSON.stringify(obj, (key, value) => {
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return '[Circular]';
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function logPacket(data: any) {
|
function logPacket(data: any) {
|
||||||
try {
|
try {
|
||||||
fs.appendFileSync(packetLogPath, `[${new Date().toISOString()}] ${JSON.stringify(data)}\n`);
|
fs.appendFileSync(
|
||||||
|
packetLogPath,
|
||||||
|
`[${new Date().toISOString()}] ${safeStringifyPacket(data)}\n`,
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export class GeminiClient {
|
|||||||
this.logger.error(`Gemini Stream API Error: ${error.message}`);
|
this.logger.error(`Gemini Stream API Error: ${error.message}`);
|
||||||
throw new Error(error.response?.data?.error?.message || error.message);
|
throw new Error(error.response?.data?.error?.message || error.message);
|
||||||
}
|
}
|
||||||
throw error;
|
// Re-throw as clean Error to avoid circular reference issues
|
||||||
|
throw error instanceof Error ? new Error(error.message) : new Error(String(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +51,12 @@ export class GeminiClient {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Gemini API Error: ${error.message} - ${JSON.stringify(error.response?.data)}`,
|
`Gemini API Error: ${error.message} - ${this.safeStringify(error.response?.data)}`,
|
||||||
);
|
);
|
||||||
throw new Error(error.response?.data?.error?.message || error.message);
|
throw new Error(error.response?.data?.error?.message || error.message);
|
||||||
}
|
}
|
||||||
throw error;
|
// Re-throw as clean Error to avoid circular reference issues
|
||||||
|
throw error instanceof Error ? new Error(error.message) : new Error(String(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +79,6 @@ export class GeminiClient {
|
|||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleAxiosError(error, 'StreamInternal');
|
this.handleAxiosError(error, 'StreamInternal');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,17 +97,38 @@ export class GeminiClient {
|
|||||||
return response.data.response || response.data;
|
return response.data.response || response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleAxiosError(error, 'GenerateInternal');
|
this.handleAxiosError(error, 'GenerateInternal');
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleAxiosError(error: any, context: string) {
|
private handleAxiosError(error: any, context: string) {
|
||||||
if (axios.isAxiosError(error)) {
|
if (axios.isAxiosError(error)) {
|
||||||
this.logger.error(
|
this.logger.error(
|
||||||
`Gemini ${context} API Error: ${error.message} - ${JSON.stringify(error.response?.data)}`,
|
`Gemini ${context} API Error: ${error.message} - ${this.safeStringify(error.response?.data)}`,
|
||||||
);
|
);
|
||||||
throw new Error(error.response?.data?.error?.message || error.message);
|
throw new Error(error.response?.data?.error?.message || error.message);
|
||||||
}
|
}
|
||||||
throw error;
|
// Re-throw as clean Error to avoid circular reference issues
|
||||||
|
throw error instanceof Error ? new Error(error.message) : new Error(String(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely stringify an object, handling circular references
|
||||||
|
*/
|
||||||
|
private safeStringify(obj: unknown): string {
|
||||||
|
if (obj === undefined || obj === null) {
|
||||||
|
return String(obj);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return JSON.stringify(obj, (key, value) => {
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (seen.has(value)) return '[Circular]';
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return '[Unserializable]';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +217,11 @@ export class ProxyService {
|
|||||||
subscriber.complete();
|
subscriber.complete();
|
||||||
});
|
});
|
||||||
|
|
||||||
upstreamStream.on('error', (err: any) => subscriber.error(err));
|
upstreamStream.on('error', (err: any) => {
|
||||||
|
// Convert to clean Error to avoid circular reference issues (socket objects)
|
||||||
|
const cleanError = err instanceof Error ? new Error(err.message) : new Error(String(err));
|
||||||
|
subscriber.error(cleanError);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,7 +376,9 @@ export class ProxyService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
upstreamStream.on('error', (err: any) => {
|
upstreamStream.on('error', (err: any) => {
|
||||||
subscriber.error(err);
|
// Convert to clean Error to avoid circular reference issues (socket objects)
|
||||||
|
const cleanError = err instanceof Error ? new Error(err.message) : new Error(String(err));
|
||||||
|
subscriber.error(cleanError);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-1
@@ -4,6 +4,33 @@ import { getAgentDir } from './paths';
|
|||||||
|
|
||||||
export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
|
export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely stringify an object, handling circular references
|
||||||
|
* This prevents "Converting circular structure to JSON" errors
|
||||||
|
* when logging objects like axios errors that contain socket references
|
||||||
|
*/
|
||||||
|
function safeStringify(obj: unknown): string {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return JSON.stringify(obj, (key, value) => {
|
||||||
|
// Handle Error objects specially
|
||||||
|
if (value instanceof Error) {
|
||||||
|
return {
|
||||||
|
name: value.name,
|
||||||
|
message: value.message,
|
||||||
|
stack: value.stack,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Handle circular references
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return '[Circular]';
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class Logger {
|
class Logger {
|
||||||
private logFilePath: string;
|
private logFilePath: string;
|
||||||
|
|
||||||
@@ -22,7 +49,7 @@ class Logger {
|
|||||||
private formatMessage(level: LogLevel, message: string, ...args: unknown[]): string {
|
private formatMessage(level: LogLevel, message: string, ...args: unknown[]): string {
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
const formattedArgs = args
|
const formattedArgs = args
|
||||||
.map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg)))
|
.map((arg) => (typeof arg === 'object' ? safeStringify(arg) : String(arg)))
|
||||||
.join(' ');
|
.join(' ');
|
||||||
return `[${timestamp}] [${level.toUpperCase()}] ${message} ${formattedArgs}`;
|
return `[${timestamp}] [${level.toUpperCase()}] ${message} ${formattedArgs}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user