Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
- Update all `from storage.` imports to `from dss.storage.` - Update `from config import config` to use `dss.settings` - Update `from auth.` imports to `from dss.auth.` - Update health check to use `dss.mcp.handler` - Fix SmartMerger import (merger.py not smart_merger.py) - Fix TranslationDictionary import path - Fix test assertion for networkx edges/links - Remove organ/body metaphors from: - API server health check - CLI status command and help text - Admin UI logger and error handler 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
302 lines
8.9 KiB
JavaScript
302 lines
8.9 KiB
JavaScript
/**
|
|
* DSS Error Handler
|
|
*
|
|
* Converts technical errors into user-friendly, actionable messages.
|
|
* Integrates with the messaging system for structured error reporting.
|
|
*
|
|
* @module error-handler
|
|
*/
|
|
|
|
import { notifyError, ErrorCode } from './messaging.js';
|
|
|
|
/**
|
|
* Error message templates with user-friendly descriptions
|
|
*/
|
|
const errorMessages = {
|
|
// Figma API Errors
|
|
figma_403: {
|
|
title: 'Access Denied',
|
|
message: 'Cannot access the Figma file. Your access credentials lack permission.',
|
|
actions: [
|
|
'Verify your Figma authentication token in Settings',
|
|
'Confirm you have access to this file in Figma',
|
|
'Check if the file still exists',
|
|
],
|
|
code: ErrorCode.FIGMA_API_ERROR,
|
|
},
|
|
figma_404: {
|
|
title: 'File Not Found',
|
|
message: 'The Figma file doesn\'t exist or is inaccessible.',
|
|
actions: [
|
|
'Double-check your Figma file key in Settings',
|
|
'Verify the file hasn\'t been deleted in Figma',
|
|
'Confirm you have access to the file in Figma',
|
|
],
|
|
code: ErrorCode.FIGMA_API_ERROR,
|
|
},
|
|
figma_401: {
|
|
title: 'Authentication Failed',
|
|
message: 'Figma authentication failed. Your access token is invalid or expired.',
|
|
actions: [
|
|
'Refresh your Figma authentication token in Settings',
|
|
'Get a fresh token from figma.com/settings (Account -> Personal Access Tokens)',
|
|
'Ensure you copied the full token without truncation',
|
|
],
|
|
code: ErrorCode.FIGMA_CONNECTION_FAILED,
|
|
},
|
|
figma_429: {
|
|
title: 'Rate Limit Exceeded',
|
|
message: 'Too many requests. Figma\'s rate limits have been triggered.',
|
|
actions: [
|
|
'Wait 1-2 minutes before trying again',
|
|
'Reduce how frequently you extract data',
|
|
],
|
|
code: ErrorCode.FIGMA_API_ERROR,
|
|
},
|
|
figma_500: {
|
|
title: 'Figma Server Error',
|
|
message: 'Figma\'s servers are experiencing issues. This is external to DSS.',
|
|
actions: [
|
|
'Wait and try again later',
|
|
'Check Figma status: status.figma.com',
|
|
],
|
|
code: ErrorCode.FIGMA_API_ERROR,
|
|
},
|
|
figma_demo: {
|
|
title: 'Invalid Configuration',
|
|
message: 'The Figma file key is configured to "demo" which doesn\'t exist in Figma.',
|
|
actions: [
|
|
'Update Settings with your real Figma file key',
|
|
'Find your file key in the Figma URL: figma.com/file/[FILE_KEY]/...',
|
|
'Use the Figma file selector in Settings',
|
|
],
|
|
code: ErrorCode.FIGMA_INVALID_KEY,
|
|
},
|
|
|
|
// API Connection Errors
|
|
api_network: {
|
|
title: 'Server Not Responding',
|
|
message: 'Cannot connect to the DSS server.',
|
|
actions: [
|
|
'Verify the server is running: curl http://localhost:3456/health',
|
|
'Restart the server: ./scripts/dss start',
|
|
'Check your network connection',
|
|
],
|
|
code: ErrorCode.SYSTEM_NETWORK,
|
|
},
|
|
api_timeout: {
|
|
title: 'Request Timeout',
|
|
message: 'The server took too long to respond.',
|
|
actions: [
|
|
'Try again in a few moments',
|
|
'Check the server logs: ./scripts/dss logs',
|
|
'Try processing smaller batches',
|
|
],
|
|
code: ErrorCode.API_TIMEOUT,
|
|
},
|
|
api_500: {
|
|
title: 'Server Error',
|
|
message: 'The server encountered an error while processing your request.',
|
|
actions: [
|
|
'Check the server logs: ./scripts/dss logs',
|
|
'Retry the operation',
|
|
'Report the issue if it persists',
|
|
],
|
|
code: ErrorCode.API_SERVER_ERROR,
|
|
},
|
|
|
|
// Validation Errors
|
|
validation_missing_field: {
|
|
title: 'Missing Required Field',
|
|
message: 'The configuration is missing required information.',
|
|
actions: [
|
|
'Fill in all fields marked as required',
|
|
'Ensure each input field contains valid information',
|
|
],
|
|
code: ErrorCode.VALIDATION_MISSING_FIELD,
|
|
},
|
|
validation_invalid_format: {
|
|
title: 'Invalid Format',
|
|
message: 'One or more configuration values have an invalid format.',
|
|
actions: [
|
|
'Verify URLs start with http:// or https://',
|
|
'Check email addresses follow standard format',
|
|
'Ensure file keys contain only letters, numbers, and hyphens',
|
|
],
|
|
code: ErrorCode.VALIDATION_INVALID_FORMAT,
|
|
},
|
|
|
|
// Generic fallback
|
|
unknown: {
|
|
title: 'Unexpected Error',
|
|
message: 'An unexpected problem occurred.',
|
|
actions: [
|
|
'Try the operation again',
|
|
'Refresh the page if the issue persists',
|
|
'Check browser console for details',
|
|
],
|
|
code: ErrorCode.SYSTEM_UNEXPECTED,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Parse error and return user-friendly message
|
|
* @param {Error} error - The error to parse
|
|
* @param {Object} context - Additional context (operation, endpoint, etc.)
|
|
* @returns {Object} Parsed error with user-friendly message
|
|
*/
|
|
export function parseError(error, context = {}) {
|
|
const errorStr = error.message || String(error);
|
|
|
|
// Figma API Errors
|
|
if (context.service === 'figma' || errorStr.includes('figma.com')) {
|
|
// Demo file key
|
|
if (context.fileKey === 'demo' || errorStr.includes('/demo/')) {
|
|
return errorMessages.figma_demo;
|
|
}
|
|
|
|
// HTTP status codes
|
|
if (errorStr.includes('403')) {
|
|
return errorMessages.figma_403;
|
|
}
|
|
if (errorStr.includes('404')) {
|
|
return errorMessages.figma_404;
|
|
}
|
|
if (errorStr.includes('401')) {
|
|
return errorMessages.figma_401;
|
|
}
|
|
if (errorStr.includes('429')) {
|
|
return errorMessages.figma_429;
|
|
}
|
|
if (errorStr.includes('500') || errorStr.includes('502') || errorStr.includes('503')) {
|
|
return errorMessages.figma_500;
|
|
}
|
|
}
|
|
|
|
// Network Errors
|
|
if (errorStr.includes('NetworkError') || errorStr.includes('Failed to fetch')) {
|
|
return errorMessages.api_network;
|
|
}
|
|
|
|
// Timeout Errors
|
|
if (errorStr.includes('timeout') || errorStr.includes('Timeout')) {
|
|
return errorMessages.api_timeout;
|
|
}
|
|
|
|
// API Server Errors
|
|
if (errorStr.includes('500') || errorStr.includes('Internal Server Error')) {
|
|
return errorMessages.api_500;
|
|
}
|
|
|
|
// Validation Errors
|
|
if (errorStr.includes('required') || errorStr.includes('missing')) {
|
|
return errorMessages.validation_missing_field;
|
|
}
|
|
if (errorStr.includes('invalid') || errorStr.includes('format')) {
|
|
return errorMessages.validation_invalid_format;
|
|
}
|
|
|
|
// Fallback
|
|
return errorMessages.unknown;
|
|
}
|
|
|
|
/**
|
|
* Format user-friendly error message
|
|
* @param {Object} parsedError - Parsed error from parseError()
|
|
* @returns {string} Formatted message
|
|
*/
|
|
export function formatErrorMessage(parsedError) {
|
|
let message = `${parsedError.title}\n\n${parsedError.message}`;
|
|
|
|
if (parsedError.actions && parsedError.actions.length > 0) {
|
|
message += '\n\nWhat to do:\n';
|
|
parsedError.actions.forEach((action, index) => {
|
|
message += `${index + 1}. ${action}\n`;
|
|
});
|
|
}
|
|
|
|
return message.trim();
|
|
}
|
|
|
|
/**
|
|
* Handle error and notify user with friendly message
|
|
* @param {Error} error - The error to handle
|
|
* @param {Object} context - Additional context
|
|
* @param {string} context.operation - What operation failed (e.g., "extract tokens")
|
|
* @param {string} context.service - Which service (e.g., "figma")
|
|
* @param {string} context.fileKey - Figma file key if applicable
|
|
*/
|
|
export function handleError(error, context = {}) {
|
|
const parsed = parseError(error, context);
|
|
|
|
// Create user-friendly message
|
|
let userMessage = parsed.title;
|
|
|
|
if (parsed.actions && parsed.actions.length > 0) {
|
|
userMessage += '. ' + parsed.actions[0]; // Show first action in notification
|
|
}
|
|
|
|
// Notify user with structured error
|
|
notifyError(userMessage, parsed.code, {
|
|
operation: context.operation,
|
|
service: context.service,
|
|
originalError: error.message,
|
|
actions: parsed.actions,
|
|
});
|
|
|
|
// Log full details to console for debugging
|
|
console.group(`[ERROR] ${parsed.title}`);
|
|
console.log('Message:', parsed.message);
|
|
if (parsed.actions) {
|
|
console.log('Actions:', parsed.actions);
|
|
}
|
|
console.log('Context:', context);
|
|
console.log('Original Error:', error);
|
|
console.groupEnd();
|
|
|
|
return parsed;
|
|
}
|
|
|
|
/**
|
|
* Try-catch wrapper with automatic error handling
|
|
* @param {Function} fn - Async function to execute
|
|
* @param {Object} context - Error context
|
|
* @returns {Promise<*>} Result of function or null on error
|
|
*/
|
|
export async function tryWithErrorHandling(fn, context = {}) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
handleError(error, context);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user-friendly HTTP status message
|
|
* @param {number} status - HTTP status code
|
|
* @returns {string} User-friendly message
|
|
*/
|
|
export function getStatusMessage(status) {
|
|
const messages = {
|
|
400: 'Bad Request - invalid input data',
|
|
401: 'Authentication Failed - invalid credentials',
|
|
403: 'Access Forbidden - permission denied',
|
|
404: 'Not Found - resource doesn\'t exist',
|
|
429: 'Rate Limited - too many requests',
|
|
500: 'Server Error - internal processing failure',
|
|
502: 'Bad Gateway - server not responding',
|
|
503: 'Service Unavailable - temporarily unable to handle requests',
|
|
};
|
|
|
|
return messages[status] || `Unknown Error - HTTP ${status}`;
|
|
}
|
|
|
|
export default {
|
|
parseError,
|
|
formatErrorMessage,
|
|
handleError,
|
|
tryWithErrorHandling,
|
|
getStatusMessage,
|
|
};
|