Compare commits

...

2 Commits

Author SHA1 Message Date
Arunavo Ray
99336e2607 chore: bump version to 2.9.2 2025-05-28 10:15:43 +05:30
Arunavo Ray
cba421d606 feat: enhance error logging for better debugging of JSON parsing issues
- Add comprehensive error logging in mirror-repo API endpoint
- Enhance HTTP client error handling with detailed response information
- Improve concurrency utility error reporting with context
- Add specific detection and guidance for JSON parsing errors
- Include troubleshooting information in error responses
- Update tests to accommodate enhanced logging

This will help users diagnose issues like 'JSON Parse error: Unexpected EOF'
by providing detailed information about what responses are being received
from the Gitea API and what might be causing the failures.
2025-05-28 10:13:41 +05:30
5 changed files with 115 additions and 44 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "gitea-mirror", "name": "gitea-mirror",
"type": "module", "type": "module",
"version": "2.9.1", "version": "2.9.2",
"engines": { "engines": {
"bun": ">=1.2.9" "bun": ">=1.2.9"
}, },

View File

@@ -70,9 +70,19 @@ export async function httpRequest<T = any>(
data = await response.json(); data = await response.json();
} catch (jsonError) { } catch (jsonError) {
const responseText = await responseClone.text(); const responseText = await responseClone.text();
console.error(`Failed to parse JSON response: ${responseText}`);
// Enhanced JSON parsing error logging
console.error("=== JSON PARSING ERROR ===");
console.error("URL:", url);
console.error("Status:", response.status, response.statusText);
console.error("Content-Type:", contentType);
console.error("Response length:", responseText.length);
console.error("Response preview (first 500 chars):", responseText.substring(0, 500));
console.error("JSON Error:", jsonError instanceof Error ? jsonError.message : String(jsonError));
console.error("========================");
throw new HttpError( throw new HttpError(
`Failed to parse JSON response: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`, `Failed to parse JSON response from ${url}: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}. Response: ${responseText.substring(0, 200)}${responseText.length > 200 ? '...' : ''}`,
response.status, response.status,
response.statusText, response.statusText,
responseText responseText

View File

@@ -67,8 +67,8 @@ describe("processInParallel", () => {
// Verify that processItem was called for each item // Verify that processItem was called for each item
expect(processItem).toHaveBeenCalledTimes(5); expect(processItem).toHaveBeenCalledTimes(5);
// Verify that console.error was called once // Verify that console.error was called (enhanced logging calls it multiple times)
expect(consoleErrorMock).toHaveBeenCalledTimes(1); expect(consoleErrorMock).toHaveBeenCalled();
} finally { } finally {
// Restore console.error // Restore console.error
console.error = originalConsoleError; console.error = originalConsoleError;
@@ -157,8 +157,8 @@ describe("processWithRetry", () => {
// Verify that onRetry was called twice (for 2 retry attempts) // Verify that onRetry was called twice (for 2 retry attempts)
expect(onRetry).toHaveBeenCalledTimes(2); expect(onRetry).toHaveBeenCalledTimes(2);
// Verify that console.error was called once // Verify that console.error was called (enhanced logging calls it multiple times)
expect(consoleErrorMock).toHaveBeenCalledTimes(1); expect(consoleErrorMock).toHaveBeenCalled();
} finally { } finally {
// Restore console.error // Restore console.error
console.error = originalConsoleError; console.error = originalConsoleError;

View File

@@ -46,11 +46,25 @@ export async function processInParallel<T, R>(
const batchResults = await Promise.allSettled(batchPromises); const batchResults = await Promise.allSettled(batchPromises);
// Process results and handle errors // Process results and handle errors
for (const result of batchResults) { for (let j = 0; j < batchResults.length; j++) {
const result = batchResults[j];
if (result.status === 'fulfilled') { if (result.status === 'fulfilled') {
results.push(result.value); results.push(result.value);
} else { } else {
console.error('Error processing item:', result.reason); const itemIndex = i + j;
console.error("=== BATCH ITEM PROCESSING ERROR ===");
console.error("Batch index:", Math.floor(i / concurrencyLimit));
console.error("Item index in batch:", j);
console.error("Global item index:", itemIndex);
console.error("Error type:", result.reason?.constructor?.name);
console.error("Error message:", result.reason instanceof Error ? result.reason.message : String(result.reason));
if (result.reason instanceof Error && result.reason.message.includes('JSON')) {
console.error("🚨 JSON parsing error in batch processing");
console.error("This indicates an API response issue from Gitea");
}
console.error("==================================");
} }
} }
} }
@@ -139,6 +153,21 @@ export async function processWithRetry<T, R>(
const delay = retryDelay * Math.pow(2, attempt - 1); const delay = retryDelay * Math.pow(2, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay)); await new Promise(resolve => setTimeout(resolve, delay));
} else { } else {
// Enhanced error logging for final failure
console.error("=== ITEM PROCESSING FAILED (MAX RETRIES EXCEEDED) ===");
console.error("Item:", getItemId ? getItemId(item) : 'unknown');
console.error("Error type:", lastError.constructor.name);
console.error("Error message:", lastError.message);
console.error("Attempts made:", maxRetries + 1);
if (lastError.message.includes('JSON')) {
console.error("🚨 JSON-related error detected in item processing");
console.error("This suggests an issue with API responses from Gitea");
}
console.error("Stack trace:", lastError.stack);
console.error("================================================");
throw lastError; throw lastError;
} }
} }

View File

@@ -165,11 +165,43 @@ export const POST: APIRoute = async ({ request }) => {
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
}); });
} catch (error) { } catch (error) {
console.error("Error mirroring repositories:", error); // Enhanced error logging for better debugging
console.error("=== ERROR MIRRORING REPOSITORIES ===");
console.error("Error type:", error?.constructor?.name);
console.error("Error message:", error instanceof Error ? error.message : String(error));
if (error instanceof Error) {
console.error("Error stack:", error.stack);
}
// Log additional context
console.error("Request details:");
console.error("- URL:", request.url);
console.error("- Method:", request.method);
console.error("- Headers:", Object.fromEntries(request.headers.entries()));
// If it's a JSON parsing error, provide more context
if (error instanceof SyntaxError && error.message.includes('JSON')) {
console.error("🚨 JSON PARSING ERROR DETECTED:");
console.error("This suggests the response from Gitea API is not valid JSON");
console.error("Common causes:");
console.error("- Gitea server returned HTML error page instead of JSON");
console.error("- Network connection interrupted");
console.error("- Gitea server is down or misconfigured");
console.error("- Authentication token is invalid");
console.error("Check your Gitea server logs and configuration");
}
console.error("=====================================");
return new Response( return new Response(
JSON.stringify({ JSON.stringify({
error: error: error instanceof Error ? error.message : "An unknown error occurred",
error instanceof Error ? error.message : "An unknown error occurred", errorType: error?.constructor?.name || "Unknown",
timestamp: new Date().toISOString(),
troubleshooting: error instanceof SyntaxError && error.message.includes('JSON')
? "JSON parsing error detected. Check Gitea server status and logs. Ensure Gitea is returning valid JSON responses."
: "Check application logs for more details"
}), }),
{ status: 500, headers: { "Content-Type": "application/json" } } { status: 500, headers: { "Content-Type": "application/json" } }
); );