Skip to main content
Glama
by elad12390
errors.py11.6 kB
"""Error translation and solution finding.""" from __future__ import annotations import re from dataclasses import dataclass @dataclass(slots=True) class ParsedError: """Represents a parsed error message.""" error_type: str message: str language: str | None framework: str | None file_path: str | None line_number: int | None key_terms: list[str] class ErrorParser: """Parses error messages and stack traces to extract key information.""" # Language detection patterns (order matters - most specific first) LANGUAGE_PATTERNS = { "rust": [ r"error\[E\d{4}\]:", r"cannot borrow", r"--> .+\.rs:\d+:\d+", ], "typescript": [ r"at .+\.tsx?:\d+:\d+", r"\.tsx?:", r"TS\d{4}:", ], "javascript": [ r"at .+\.jsx?:\d+:\d+", r"\.jsx?:", r"node_modules", r"Cannot read property", r"is not defined", ], "python": [ r"File \"(.+)\.py\"", r"Traceback \(most recent call last\)", r"(ImportError|AttributeError|ModuleNotFoundError)", ], "java": [ r"at .+\.java:\d+", r"Exception in thread", r"(NullPointerException|IllegalArgumentException)", ], "go": [ r"panic:", r"goroutine \d+", r".+\.go:\d+", ], } # Framework detection patterns FRAMEWORK_PATTERNS = { "react": [r"react", r"jsx", r"useState", r"useEffect"], "vue": [r"vue", r"@vue", r"composition-api"], "angular": [r"angular", r"@angular", r"ngOnInit"], "django": [r"django", r"django\."], "flask": [r"flask", r"werkzeug"], "fastapi": [r"fastapi", r"pydantic", r"starlette"], "express": [r"express", r"app\.get", r"app\.post"], "nextjs": [r"next", r"getServerSideProps", r"getStaticProps"], } # Common error patterns (checked in order - most specific first) ERROR_TYPE_PATTERNS = { "python": { "AttributeError": r"AttributeError: '(.+)' object has no attribute '(.+)'", "TypeError": r"TypeError: (.+)", "ImportError": r"(ImportError|ModuleNotFoundError): (.+)", "ValueError": r"ValueError: (.+)", "KeyError": r"KeyError: (.+)", }, "javascript": { # Web-specific errors (check first - most specific) "CORS Error": r"CORS policy|Access-Control-Allow-Origin|No.*Access-Control", "Fetch Error": r"fetch.*failed|Failed to fetch|NetworkError", "Cannot read property": r"Cannot read propert(?:y|ies) ['\"](.+?)['\"] of", "undefined is not": r"undefined is not (a function|an object)", "null is not": r"null is not (a function|an object)", # Standard errors (check after specific ones) "TypeError": r"TypeError: (.+)", "ReferenceError": r"ReferenceError: (.+)", "SyntaxError": r"SyntaxError: (.+)", "RangeError": r"RangeError: (.+)", }, "typescript": { "CORS Error": r"CORS policy|Access-Control-Allow-Origin", "Fetch Error": r"fetch.*failed|Failed to fetch", "Cannot read property": r"Cannot read propert(?:y|ies) ['\"](.+?)['\"] of", "TypeError": r"TypeError: (.+)", "ReferenceError": r"ReferenceError: (.+)", "SyntaxError": r"SyntaxError: (.+)", }, "rust": { "borrow error": r"borrow of moved value", "cannot borrow": r"cannot borrow", "lifetime error": r"lifetime (.+) may not live long enough", "type mismatch": r"expected (.+), found (.+)", "E0382": r"error\[E0382\]", "E0502": r"error\[E0502\]", "E0308": r"error\[E0308\]", }, } def parse( self, error_message: str, language: str | None = None, framework: str | None = None, ) -> ParsedError: """ Parse an error message to extract key information. Args: error_message: The error text or stack trace language: Programming language (auto-detected if None) framework: Framework name (auto-detected if None) Returns: ParsedError with extracted information """ # Detect language if not provided if not language: language = self._detect_language(error_message) # Detect framework if not provided if not framework: framework = self._detect_framework(error_message) # Extract error type error_type = self._extract_error_type(error_message, language) # Extract file path and line number file_path, line_number = self._extract_location(error_message) # Extract key terms key_terms = self._extract_key_terms(error_message, error_type) # Clean message message = self._clean_message(error_message) return ParsedError( error_type=error_type or "Unknown Error", message=message, language=language, framework=framework, file_path=file_path, line_number=line_number, key_terms=key_terms, ) def _detect_language(self, text: str) -> str | None: """Detect programming language from error message.""" scores = {} for lang, patterns in self.LANGUAGE_PATTERNS.items(): score = sum(1 for pattern in patterns if re.search(pattern, text, re.IGNORECASE)) if score > 0: scores[lang] = score if scores: return max(scores, key=scores.get) return None def _detect_framework(self, text: str) -> str | None: """Detect framework from error message.""" text_lower = text.lower() for framework, patterns in self.FRAMEWORK_PATTERNS.items(): if any(re.search(pattern, text_lower) for pattern in patterns): return framework return None def _extract_error_type(self, text: str, language: str | None) -> str | None: """Extract the error type/name.""" # Check for common web errors first (language-agnostic) web_error_patterns = { "CORS Error": r"CORS policy|Access-Control-Allow-Origin|No.*Access-Control", "Fetch Error": r"fetch.*failed|Failed to fetch|NetworkError", "Cannot read property": r"Cannot read propert(?:y|ies) ['\"](.+?)['\"] of", } for error_name, pattern in web_error_patterns.items(): if re.search(pattern, text, re.IGNORECASE): return error_name # Language-specific extraction if language is detected if language and language in self.ERROR_TYPE_PATTERNS: for error_name, pattern in self.ERROR_TYPE_PATTERNS[language].items(): if re.search(pattern, text, re.IGNORECASE): return error_name # Generic error type extraction as fallback match = re.search(r"([\w]+Error|[\w]+Exception):", text) if match: return match.group(1) return None def _extract_location(self, text: str) -> tuple[str | None, int | None]: """Extract file path and line number.""" # Python format: File "path.py", line 123 match = re.search(r'File "(.+?)", line (\d+)', text) if match: return match.group(1), int(match.group(2)) # JavaScript/TypeScript format: at path.js:123:45 match = re.search(r"at (.+?):(\d+):\d+", text) if match: return match.group(1), int(match.group(2)) # Rust format: --> path.rs:123:45 match = re.search(r"--> (.+?):(\d+):\d+", text) if match: return match.group(1), int(match.group(2)) return None, None def _extract_key_terms(self, text: str, error_type: str | None) -> list[str]: """Extract key terms for searching.""" terms = set() # Use set to avoid duplicates # Important web/tech terms to always capture important_terms = [ "CORS", "cors", "fetch", "async", "await", "Promise", "undefined", "null", "map", "filter", "reduce", "Access-Control-Allow-Origin", "XMLHttpRequest", "module", "import", "export", "require", ] # Extract important terms first text_lower = text.lower() for term in important_terms: if term.lower() in text_lower: terms.add(term) if len(terms) >= 3: # Limit important terms to 3 break # Extract quoted strings (often property names or specific values) quoted = re.findall(r"'([^']+)'|\"([^\"]+)\"", text) for match in quoted: term = match[0] or match[1] # Keep short important terms, ignore paths if term and "/" not in term and "\\" not in term: # Allow short terms for property names like "map", "id", etc. if len(term) <= 15: # Reasonable property name length if not error_type or term.lower() != error_type.lower(): terms.add(term) if len(terms) >= 5: break # Extract technical terms (CamelCase, snake_case) technical = re.findall(r"\b[A-Z][a-z]+(?:[A-Z][a-z]+)+\b|\b\w+_\w+\b", text) # Don't exclude undefined/null - they're useful search terms for term in technical: if not error_type or term != error_type: terms.add(term) if len(terms) >= 5: break return list(terms)[:5] # Return max 5 unique key terms def _clean_message(self, text: str) -> str: """Clean and extract the core error message.""" # Try to find the main error line lines = text.strip().split("\n") # Look for the error line (usually contains "Error:" or "Exception:") for line in reversed(lines): if re.search(r"(Error|Exception):", line): return line.strip() # If no error line found, return first non-empty line for line in lines: if line.strip(): return line.strip() return text.strip() def build_search_query(self, parsed: ParsedError) -> str: """Build an optimized search query from parsed error.""" parts = [] # Add language if parsed.language: parts.append(parsed.language) # Add framework if parsed.framework: parts.append(parsed.framework) # Add error type if parsed.error_type and parsed.error_type != "Unknown Error": parts.append(parsed.error_type) # Add key terms (limit to avoid overly specific queries) parts.extend(parsed.key_terms[:2]) # Build query - don't use site filter as SearXNG handles this better # with its category-based engine selection query = " ".join(parts) # Add "error" or "fix" to help guide results if query: # Only add if we have content query += " error fix" return query

Implementation Reference

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/elad12390/web-research-assistant'

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