Skip to main content
Glama
error_formatter.py25.3 kB
"""Error formatting utilities for better user feedback.""" from typing import Any class ErrorFormatter: """Format errors with actionable guidance for users.""" # Common error patterns and their explanations ERROR_PATTERNS = { "active directory web services": { "title": "Active Directory Web Services Not Running", "meaning": ( "The Domain Controller VM is running, but AD Web Services hasn't " "started yet. This is normal during initial DC setup." ), "severity": "transient", "actions": [ "- Wait 5-10 minutes for AD services to fully initialize", "- Ludus will automatically retry the failed tasks", "- Check status: ludus.quick_status", "- View logs: ludus.get_full_logs" ], "note": "This is a TRANSIENT error - it typically resolves automatically.", "next_steps": [ "1. Check status in 5 minutes: ludus.quick_status", "2. If still failing after 15 min: ludus.check_deployment_health", "3. View full diagnostics: ludus.get_full_logs" ] }, "template not found": { "title": "VM Template Not Found", "meaning": ( "The specified VM template doesn't exist in your Ludus environment." ), "severity": "error", "actions": [ "- List available templates: ludus.list_templates", "- Check template name spelling", "- Verify template is installed in Ludus", "- Update configuration with correct template name" ], "note": "This requires configuration changes before redeploying.", "next_steps": [ "1. List templates: ludus.list_templates", "2. Fix config with correct template", "3. Validate config: ludus.validate_config(config)", "4. Redeploy: ludus.deploy_range" ] }, "role not found": { "title": "Ansible Role Not Installed", "meaning": ( "An Ansible role required by your configuration is not installed." ), "severity": "warning", "actions": [ "- Ludus may auto-install the role if available", "- List installed roles: ludus.list_roles", "- Install manually if needed", "- Or remove role from configuration" ], "note": "Ludus often auto-installs missing roles during deployment.", "next_steps": [ "1. Check if deployment continues (auto-install)", "2. List roles: ludus.list_roles", "3. If deployment fails, remove role or install manually" ] }, "connection refused": { "title": "Network Connection Refused", "meaning": ( "Ludus cannot connect to a VM. The VM may not be fully booted or " "network services may not be ready." ), "severity": "warning", "actions": [ "- Wait for VMs to fully boot", "- Check VM power state: ludus.get_range", "- Verify network configuration", "- Check firewall rules" ], "note": "VMs may need a few minutes to boot and start network services.", "next_steps": [ "1. Wait 2-3 minutes for VMs to boot", "2. Check VM status: ludus.get_range", "3. Review network config if persists" ] }, "timeout": { "title": "Operation Timeout", "meaning": ( "An operation took longer than expected. This could be due to slow " "VM boot, network latency, or resource constraints." ), "severity": "warning", "actions": [ "- Wait - operation may still complete", "- Check host resources (CPU, RAM, disk)", "- Monitor deployment: ludus.get_deployment_status", "- Ludus will retry automatically" ], "note": "Timeouts often resolve themselves. Ludus will retry.", "next_steps": [ "1. Monitor status: ludus.quick_status", "2. Check health: ludus.check_deployment_health", "3. If persistent, check host resources" ] }, "unreachable": { "title": "Host Unreachable", "meaning": ( "A VM is unreachable by Ansible. The VM may be powered off, " "not fully booted, or having network issues." ), "severity": "warning", "actions": [ "- Check VM power state: ludus.get_range", "- Verify VM is fully booted (check console)", "- Check network connectivity", "- Review firewall rules" ], "note": "VMs need time to boot before Ansible can connect.", "next_steps": [ "1. Check VMs are running: ludus.get_range", "2. Wait 3-5 minutes for boot", "3. Check logs: ludus.get_full_logs" ] }, "fatal": { "title": "Fatal Error", "meaning": ( "A critical error occurred that cannot be automatically recovered." ), "severity": "error", "actions": [ "- Review full logs: ludus.get_full_logs", "- Identify root cause", "- Fix configuration", "- Consider destroying and redeploying" ], "note": "This may require manual intervention.", "next_steps": [ "1. Get full logs: ludus.get_full_logs", "2. Fix identified issues", "3. Consider: ludus.should_destroy_range", "4. Redeploy with fixes" ] }, "authentication failed": { "title": "API Authentication Failed", "meaning": ( "The provided API key is invalid, expired, or not set correctly." ), "severity": "error", "actions": [ "- Verify LUDUS_API_KEY is set correctly", "- Generate new API key: ludus user apikey", "- Check API key has not expired", "- Ensure no extra whitespace in key" ], "note": "API keys can be regenerated from the Ludus CLI.", "next_steps": [ "1. Run: ludus user apikey (to get/regenerate key)", "2. Update LUDUS_API_KEY environment variable", "3. Restart MCP server", "4. Test connection: health_check" ] }, "permission denied": { "title": "Permission Denied", "meaning": ( "Your user account does not have permission for this operation. " "Admin privileges may be required." ), "severity": "error", "actions": [ "- Check if operation requires admin privileges", "- Verify you're using the correct user", "- Contact Ludus administrator", "- Review Ludus role permissions" ], "note": "Some operations (user management, admin templates) require admin role.", "next_steps": [ "1. Check your user role: ludus.get_user", "2. Contact admin if elevated access needed", "3. Use non-admin alternatives if available" ] }, "cannot connect": { "title": "Cannot Connect to Ludus Server", "meaning": ( "The MCP server cannot reach the Ludus API. The server may be down, " "the URL may be incorrect, or there may be network issues." ), "severity": "error", "actions": [ "- Verify LUDUS_API_URL is correct", "- Check Ludus server is running", "- Verify network connectivity/VPN", "- Check firewall rules allow connection" ], "note": "Ensure you can reach the Ludus URL from your machine.", "next_steps": [ "1. Ping/curl the Ludus server URL", "2. Verify LUDUS_API_URL setting", "3. Check VPN connection if required", "4. Test: health_check" ] }, "rate limit": { "title": "Rate Limit Exceeded", "meaning": ( "Too many API requests have been made in a short time. " "The rate limiter is protecting the Ludus server." ), "severity": "warning", "actions": [ "- Wait a few seconds before retrying", "- Reduce request frequency", "- Check if requests are being duplicated", "- Consider batching operations" ], "note": "The rate limiter resets automatically. Wait and retry.", "next_steps": [ "1. Wait 30-60 seconds", "2. Retry the operation", "3. If persistent, reduce operation frequency" ] }, "ssl": { "title": "SSL/TLS Certificate Error", "meaning": ( "There's an issue with the SSL certificate. This is common in lab " "environments using self-signed certificates." ), "severity": "warning", "actions": [ "- For self-signed certs: Set LUDUS_SSL_VERIFY=false", "- For valid certs: Verify certificate chain", "- Check certificate hasn't expired", "- Verify URL uses correct protocol (https vs http)" ], "note": "Self-signed certs are common in lab environments. SSL verify is disabled by default.", "next_steps": [ "1. Check LUDUS_SSL_VERIFY setting", "2. Verify LUDUS_API_URL protocol", "3. If using valid cert, check certificate validity" ] }, "disk space": { "title": "Insufficient Disk Space", "meaning": ( "The Ludus host or VM storage is running low on disk space. " "This can prevent VM creation and deployment." ), "severity": "error", "actions": [ "- Check Proxmox storage usage", "- Delete unused VMs and templates", "- Remove old snapshots", "- Expand storage if possible" ], "note": "VMs and templates require significant disk space.", "next_steps": [ "1. Delete unused ranges: ludus.delete_range", "2. Remove old snapshots: ludus.remove_snapshot", "3. Check Proxmox storage dashboard", "4. Contact admin if storage expansion needed" ] }, "memory": { "title": "Insufficient Memory", "meaning": ( "Not enough RAM available on the Ludus host to deploy VMs. " "Reduce VM count or RAM allocation per VM." ), "severity": "error", "actions": [ "- Reduce number of VMs in range", "- Lower RAM allocation per VM", "- Power off unused ranges", "- Check host memory usage in Proxmox" ], "note": "Each VM requires dedicated RAM. Plan capacity accordingly.", "next_steps": [ "1. Power off unused ranges: ludus.power_off_range", "2. Use minimal resource profile", "3. Reduce VM count in configuration", "4. Check Proxmox memory dashboard" ] }, "winrm": { "title": "WinRM Connection Failed", "meaning": ( "Cannot connect to Windows VM via WinRM. The WinRM service " "may not be running or properly configured." ), "severity": "warning", "actions": [ "- Wait for Windows VM to fully boot", "- WinRM starts after initial setup", "- Check firewall allows WinRM (5985/5986)", "- Verify Windows credentials" ], "note": "Windows VMs need time to boot and start WinRM service.", "next_steps": [ "1. Wait 5-10 minutes for Windows to boot fully", "2. Check VM power state: ludus.get_range", "3. Verify WinRM ports are open", "4. Review deployment logs: ludus.get_full_logs" ] }, "ssh": { "title": "SSH Connection Failed", "meaning": ( "Cannot connect to Linux VM via SSH. The VM may not be booted, " "SSH service not running, or firewall blocking connection." ), "severity": "warning", "actions": [ "- Wait for Linux VM to fully boot", "- Verify SSH service is enabled", "- Check firewall allows SSH (port 22)", "- Verify SSH key/credentials" ], "note": "Linux VMs typically boot faster but still need initialization time.", "next_steps": [ "1. Wait 2-3 minutes for boot", "2. Check VM status: ludus.get_range", "3. Get SSH config: ludus.get_range_sshconfig", "4. Verify network connectivity" ] }, "already exists": { "title": "Resource Already Exists", "meaning": ( "The resource you're trying to create already exists. " "This could be a range, snapshot, template, or user." ), "severity": "warning", "actions": [ "- Use a different name", "- Delete existing resource first", "- Update existing instead of create", "- Check if existing resource meets your needs" ], "note": "Resource names must be unique within their scope.", "next_steps": [ "1. List existing resources", "2. Choose a unique name", "3. Or delete existing: ludus.delete_range/remove_snapshot" ] }, "not found": { "title": "Resource Not Found", "meaning": ( "The requested resource doesn't exist. It may have been deleted, " "never created, or the name is incorrect." ), "severity": "error", "actions": [ "- Verify resource name spelling", "- Check if resource was deleted", "- List available resources", "- Create the resource if needed" ], "note": "Double-check resource names - they are case-sensitive.", "next_steps": [ "1. List resources: ludus.get_range, ludus.list_templates", "2. Verify exact name/ID", "3. Create resource if needed" ] }, "proxmox": { "title": "Proxmox Error", "meaning": ( "An error occurred in the Proxmox virtualization layer. " "This could be a storage, network, or VM operation issue." ), "severity": "error", "actions": [ "- Check Proxmox web interface for details", "- Review Proxmox logs", "- Verify Proxmox cluster health", "- Check storage and network status" ], "note": "Proxmox errors often require admin-level troubleshooting.", "next_steps": [ "1. Access Proxmox web UI", "2. Check cluster status", "3. Review task history for errors", "4. Contact Ludus/Proxmox admin" ] } } @classmethod def format_error(cls, error_text: str, context: dict[str, Any] | None = None) -> str: """ Format an error with helpful guidance. Args: error_text: Error message or log text context: Additional context (range state, etc.) Returns: Formatted error message with guidance """ error_lower = error_text.lower() # Find matching error pattern pattern_info = None for pattern, info in cls.ERROR_PATTERNS.items(): if pattern in error_lower: pattern_info = info break if not pattern_info: # Generic error formatting return cls._format_generic_error(error_text, context) # Format known error lines = [] lines.append("") lines.append("[ERROR] " + pattern_info["title"]) lines.append("") lines.append("What this means:") lines.append(" " + pattern_info["meaning"]) lines.append("") lines.append("What to do:") for action in pattern_info["actions"]: lines.append(" " + action) lines.append("") lines.append(pattern_info["note"]) lines.append("") lines.append("Next steps:") for step in pattern_info["next_steps"]: lines.append(" " + step) lines.append("") # Add context if available if context: range_state = context.get("range_state") if range_state: lines.append(f"Current Range State: {range_state}") lines.append("") return "\n".join(lines) @classmethod def _format_generic_error(cls, error_text: str, context: dict[str, Any] | None = None) -> str: """Format a generic error without specific guidance.""" lines = [] lines.append("") lines.append("[ERROR] Deployment Error") lines.append("") lines.append("Error details:") lines.append(" " + error_text[:500]) # Limit error text length lines.append("") lines.append("Suggested actions:") lines.append(" - Check full logs: ludus.get_full_logs") lines.append(" - Check health: ludus.check_deployment_health") lines.append(" - Review configuration") lines.append(" - Check Ludus documentation") lines.append("") if context: range_state = context.get("range_state") if range_state: lines.append(f"Current Range State: {range_state}") lines.append("") return "\n".join(lines) @classmethod def format_validation_errors(cls, errors: list[Any], warnings: list[Any] | None = None) -> str: """ Format validation errors and warnings. Args: errors: List of validation errors warnings: Optional list of warnings Returns: Formatted validation report """ lines = [] lines.append("") lines.append("╔" + "═" * 63 + "╗") lines.append("║" + " Configuration Validation Report".ljust(63) + "║") lines.append("╚" + "═" * 63 + "╝") lines.append("") if errors: lines.append("[ERROR] ERRORS (must fix before deployment):") lines.append("") for error in errors: field = error.get("field", "unknown") message = error.get("message", "") lines.append(f" • Field: {field}") lines.append(f" {message}") lines.append("") if warnings: lines.append("[WARNING] WARNINGS (recommended to review):") lines.append("") for warning in warnings: field = warning.get("field", "unknown") message = warning.get("message", "") lines.append(f" • Field: {field}") lines.append(f" {message}") lines.append("") if not errors: lines.append("[OK] No errors found!") lines.append("") lines.append("Configuration is valid and ready to deploy.") lines.append("") lines.append("Next steps:") if errors: lines.append(" 1. Fix the errors listed above") lines.append(" 2. Validate again: ludus.validate_config(config)") lines.append(" 3. Deploy: ludus.deploy_range or ludus.smart_deploy") else: lines.append(" 1. Preview scenario: ludus.preview_scenario") lines.append(" 2. Deploy: ludus.smart_deploy (recommended)") lines.append(" Or: ludus.deploy_range") lines.append("") return "\n".join(lines) @classmethod def format_deployment_failure(cls, logs: str, range_state: str) -> str: """ Format a deployment failure message with guidance. Args: logs: Deployment logs range_state: Current range state Returns: Formatted failure message """ lines = [] lines.append("") lines.append("╔" + "═" * 63 + "╗") lines.append("║" + " Deployment Failed".ljust(63) + "║") lines.append("╚" + "═" * 63 + "╝") lines.append("") lines.append(f"Current State: {range_state}") lines.append("") # Analyze logs for known patterns found_patterns = [] for pattern, info in cls.ERROR_PATTERNS.items(): if pattern in logs.lower(): found_patterns.append((pattern, info)) if found_patterns: lines.append("Identified Issues:") lines.append("") for pattern, info in found_patterns: lines.append(f" • {info['title']}") severity_icon = "[WARNING]" if info["severity"] in ["warning", "transient"] else "[ERROR]" lines.append(f" {severity_icon} Severity: {info['severity']}") lines.append(f" {info['meaning']}") lines.append("") # Provide guidance for the most severe issue primary_issue = found_patterns[0][1] lines.append("Recommended Actions:") for action in primary_issue["actions"]: lines.append(" " + action) lines.append("") lines.append("Next Steps:") for step in primary_issue["next_steps"]: lines.append(" " + step) else: # Generic failure guidance lines.append("No specific error pattern identified.") lines.append("") lines.append("Recommended Actions:") lines.append(" - Review full logs: ludus.get_full_logs") lines.append(" - Check deployment health: ludus.check_deployment_health") lines.append(" - Get recovery guidance: ludus.recover_deployment") lines.append("") return "\n".join(lines) def format_success_message(scenario_key: str, range_info: dict) -> str: """ Format a deployment success message. Args: scenario_key: Scenario that was deployed range_info: Range information Returns: Formatted success message """ lines = [] lines.append("") lines.append("╔" + "═" * 63 + "╗") lines.append("║" + " Deployment Complete!".ljust(63) + "║") lines.append("╚" + "═" * 63 + "╝") lines.append("") lines.append(f"Scenario: {scenario_key}") lines.append(f"VMs: {range_info.get('numberOfVMs', 0)}") lines.append("") # List VMs vms = range_info.get("VMs", []) if vms: lines.append("Your lab is ready:") for vm in vms: hostname = vm.get("hostname", "unknown") ip = vm.get("ip", "unknown") role = vm.get("role", "") lines.append(f" [OK] {hostname} ({ip}){' - ' + role if role else ''}") lines.append("") lines.append("Access Info:") lines.append(" • RDP configs: ludus.get_range_rdpconfigs") lines.append(" • SSH config: ludus.get_range_sshconfig") lines.append(" • Range details: ludus.get_range") lines.append("") lines.append("Next Steps:") lines.append(" 1. Start testing: ludus.start_testing") lines.append(" 2. Create snapshot: ludus.snapshot_host") lines.append(" 3. Check SIEM: ludus.get_siem_info (if SIEM deployed)") lines.append("") lines.append("Ready to start testing!") lines.append("") return "\n".join(lines)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tjnull/Ludus-FastMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server