Fix webhook error handling with reliable error display

- API now properly detects N8N errors in 200 responses (OpenAI quota, etc.)
- Returns 400 status with simple "Agent unavailable" message for webhook failures
- Frontend shows errors in results container instead of disappearing toast notifications
- Users now see clear, persistent error messages when agents fail
- Keeps success toasts working, only fixes error display

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude 2025-08-15 12:38:43 +05:30
parent a46b07e371
commit 838927f316
3 changed files with 65 additions and 13 deletions

View File

@ -102,18 +102,35 @@ def execute_agent(request):
# Store webhook response # Store webhook response
execution.webhook_response = response.json() if response.headers.get('content-type', '').startswith('application/json') else {'raw': response.text} execution.webhook_response = response.json() if response.headers.get('content-type', '').startswith('application/json') else {'raw': response.text}
if response.status_code == 200: # Check if response contains N8N error indicators
has_error = False
if response.status_code == 200 and execution.webhook_response:
# Check for N8N error patterns
if isinstance(execution.webhook_response, dict):
if 'errorMessage' in execution.webhook_response or 'error' in execution.webhook_response:
has_error = True
if response.status_code == 200 and not has_error:
execution.status = 'completed' execution.status = 'completed'
execution.output_data = execution.webhook_response execution.output_data = execution.webhook_response
else:
execution.status = 'failed'
execution.error_message = f"Webhook returned {response.status_code}: {response.text[:500]}"
execution.completed_at = timezone.now() execution.completed_at = timezone.now()
execution.save() execution.save()
serializer = AgentExecutionSerializer(execution) serializer = AgentExecutionSerializer(execution)
return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
execution.status = 'failed'
if has_error:
error_msg = execution.webhook_response.get('errorMessage', 'Webhook execution failed')
execution.error_message = f"N8N Error: {error_msg[:500]}"
else:
execution.error_message = f"Webhook returned {response.status_code}: {response.text[:500]}"
execution.completed_at = timezone.now()
execution.save()
return Response({
'error': 'Agent is temporarily unavailable. Please try again later.'
}, status=status.HTTP_400_BAD_REQUEST)
except requests.RequestException as e: except requests.RequestException as e:
execution.status = 'failed' execution.status = 'failed'

View File

@ -57,7 +57,7 @@ class AgentsCore extends WorkflowsCore {
} catch (error) { } catch (error) {
console.error('Form submission error:', error); console.error('Form submission error:', error);
this.constructor.hideProcessing(); this.constructor.hideProcessing();
this.constructor.showToast('❌ Connection error. Please try again.', 'error'); this.showErrorMessage('❌ Agent is temporarily unavailable. Please try again later.');
this.resetSubmitButton(); this.resetSubmitButton();
} }
} }
@ -84,7 +84,7 @@ class AgentsCore extends WorkflowsCore {
} catch (error) { } catch (error) {
console.error('Agent execution error:', error); console.error('Agent execution error:', error);
this.constructor.hideProcessing(); this.constructor.hideProcessing();
this.constructor.showToast(`${error.message}`, 'error'); this.showErrorMessage('❌ Agent is temporarily unavailable. Please try again later.');
this.resetSubmitButton(); this.resetSubmitButton();
} }
} }
@ -545,6 +545,36 @@ class AgentsCore extends WorkflowsCore {
} }
} }
/**
* Show error message in results container
*/
showErrorMessage(message) {
const resultsContainer = document.getElementById('resultsContainer');
const resultsContent = document.getElementById('resultsContent');
if (resultsContainer && resultsContent) {
resultsContent.innerHTML = `
<div style="
background: #fee2e2;
border: 1px solid #fecaca;
border-radius: 8px;
padding: 20px;
text-align: center;
color: #dc2626;
font-size: 16px;
font-weight: 500;
margin: 20px 0;
">
${message}
</div>
`;
// Show results container
resultsContainer.style.display = 'block';
resultsContainer.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
/** /**
* Reset submit button to original state * Reset submit button to original state
*/ */

View File

@ -32,8 +32,9 @@ class WorkflowsCore {
position: fixed; position: fixed;
top: 20px; top: 20px;
right: 20px; right: 20px;
z-index: 10000; z-index: 99999;
max-width: 400px; max-width: 400px;
pointer-events: none;
`; `;
// Safely append to body // Safely append to body
@ -61,6 +62,9 @@ class WorkflowsCore {
display: flex; display: flex;
align-items: center; align-items: center;
animation: slideIn 0.3s ease-out; animation: slideIn 0.3s ease-out;
pointer-events: auto;
position: relative;
z-index: 100000;
`; `;
// Add icon and message // Add icon and message
@ -94,12 +98,13 @@ class WorkflowsCore {
toastContainer.appendChild(toast); toastContainer.appendChild(toast);
// Auto-remove after 5 seconds // Auto-remove after longer time for errors
const timeout = type === 'error' ? 8000 : 5000; // 8 seconds for errors, 5 for others
setTimeout(() => { setTimeout(() => {
if (toast.parentElement) { if (toast.parentElement) {
toast.remove(); toast.remove();
} }
}, 5000); }, timeout);
} }
/** /**