mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 08:15:17 +00:00
fix(glmt): handle 401 errors and headers-already-sent exception (#30)
- add res.headersSent check before writing error headers - write error as SSE event when headers already sent (streaming mode) - add parseUpstreamError() for user-friendly error messages - return proper HTTP status codes (401, 403, 429, 502, 504) - clear auth error: check ANTHROPIC_AUTH_TOKEN in config Fixes #26
This commit is contained in:
1 parent
6b846019b7
commit
c95338232a
1 file changed
+90
-3
+90
-3
@@ -208,12 +208,25 @@ export class GlmtProxy {
|
|||||||
const duration = Date.now() - startTime;
|
const duration = Date.now() - startTime;
|
||||||
this.log(`Request failed after ${duration}ms: ${err.message}`);
|
this.log(`Request failed after ${duration}ms: ${err.message}`);
|
||||||
|
|
||||||
res.writeHead(500, { 'Content-Type': 'application/json' });
|
// Parse error to provide clearer messages
|
||||||
|
const errorInfo = this.parseUpstreamError(err);
|
||||||
|
|
||||||
|
// Check if headers already sent (streaming mode may have started)
|
||||||
|
if (res.headersSent) {
|
||||||
|
// Headers already sent, write error as SSE event and close
|
||||||
|
this.log('Headers already sent, writing error as SSE event');
|
||||||
|
res.write(`event: error\n`);
|
||||||
|
res.write(`data: ${JSON.stringify({ error: errorInfo })}\n\n`);
|
||||||
|
res.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.writeHead(errorInfo.statusCode, { 'Content-Type': 'application/json' });
|
||||||
res.end(
|
res.end(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
error: {
|
error: {
|
||||||
type: 'proxy_error',
|
type: errorInfo.type,
|
||||||
message: err.message,
|
message: errorInfo.message,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -526,6 +539,80 @@ export class GlmtProxy {
|
|||||||
console.error(`[glmt-proxy] ${message}`);
|
console.error(`[glmt-proxy] ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse upstream error and return user-friendly error info
|
||||||
|
*/
|
||||||
|
private parseUpstreamError(err: Error): {
|
||||||
|
statusCode: number;
|
||||||
|
type: string;
|
||||||
|
message: string;
|
||||||
|
} {
|
||||||
|
const errorMessage = err.message;
|
||||||
|
|
||||||
|
// Check for 401 Unauthorized / auth token missing
|
||||||
|
if (
|
||||||
|
errorMessage.includes('401') ||
|
||||||
|
errorMessage.toLowerCase().includes('unauthorized') ||
|
||||||
|
errorMessage.toLowerCase().includes('authorization token missing') ||
|
||||||
|
errorMessage.toLowerCase().includes('invalid token')
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
statusCode: 401,
|
||||||
|
type: 'authentication_error',
|
||||||
|
message:
|
||||||
|
'Authorization token missing or invalid. Please check your ANTHROPIC_AUTH_TOKEN environment variable in ~/.ccs/config.json or settings.json.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 403 Forbidden
|
||||||
|
if (errorMessage.includes('403') || errorMessage.toLowerCase().includes('forbidden')) {
|
||||||
|
return {
|
||||||
|
statusCode: 403,
|
||||||
|
type: 'permission_error',
|
||||||
|
message: 'Access forbidden. Your token may not have permission to access this resource.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for 429 Rate limit
|
||||||
|
if (errorMessage.includes('429') || errorMessage.toLowerCase().includes('rate limit')) {
|
||||||
|
return {
|
||||||
|
statusCode: 429,
|
||||||
|
type: 'rate_limit_error',
|
||||||
|
message: 'Rate limit exceeded. Please wait before making more requests.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for timeout
|
||||||
|
if (errorMessage.toLowerCase().includes('timeout')) {
|
||||||
|
return {
|
||||||
|
statusCode: 504,
|
||||||
|
type: 'timeout_error',
|
||||||
|
message: 'Request timed out. The upstream server took too long to respond.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for connection errors
|
||||||
|
if (
|
||||||
|
errorMessage.toLowerCase().includes('econnrefused') ||
|
||||||
|
errorMessage.toLowerCase().includes('enotfound') ||
|
||||||
|
errorMessage.toLowerCase().includes('connection')
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
statusCode: 502,
|
||||||
|
type: 'connection_error',
|
||||||
|
message:
|
||||||
|
'Failed to connect to upstream server. Please check your network and ANTHROPIC_BASE_URL configuration.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default: proxy error
|
||||||
|
return {
|
||||||
|
statusCode: 500,
|
||||||
|
type: 'proxy_error',
|
||||||
|
message: errorMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main entry point
|
// Main entry point
|
||||||
|
|||||||
Reference in new issue
Block a user