mirror of
https://github.com/LukeHagar/volar-docs.git
synced 2025-12-06 04:22:01 +00:00
5.5 KiB
5.5 KiB
Error Handling & Resilience Playbook
Language servers run continuously inside users’ editors and CI pipelines. Robust error handling keeps them stable even when schemas fail to download, configuration is malformed, or files vanish mid-request. This guide catalogs every failure mode we’ve encountered and how to mitigate it gracefully.
Guiding Principles
- Fail soft – surface actionable warnings, but keep the server alive.
- Fallbacks everywhere – default to cached schemas, previous configuration, or safe defaults.
- Short-circuit – bail out early when prerequisites (schema, file content) aren’t available rather than cascading errors.
Common Failure Modes & Mitigations
Schema Fetch Failures
Symptoms: diagnostics missing, completion items blank, repeated network errors.
Mitigations:
- Cache schemas on disk (
~/.cache/volar-schemas) or in memory (Map). - Retry with exponential backoff; after a few retries, fall back to last known schema.
- Log via
connection.console.warn, notify the user if failure persists. - Allow users to disable remote fetches (
config.schemas.allowRemote = false) for air-gapped environments.
async function fetchSchema(uri: string) {
try {
const response = await fetch(uri);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.text();
} catch (err) {
log('warn', 'Schema fetch failed', { uri, err: String(err) });
return cache.get(uri) ?? DEFAULT_SCHEMA;
}
}
Missing Files / Race Conditions
When editors delete or move files while diagnostics are running:
- Re-fetch the document from
server.documentsright before publishing diagnostics, and ensure the version matches. - Wrap file reads in
try/catchand skip processing if the file disappears.
Invalid User Configuration
If users supply malformed settings:
- Validate against a JSON schema; on failure, log and revert to defaults.
- Notify users via
connection.window.showErrorMessage(“InvalidvolarJsonYaml.schemas: expected array, received string”). - Never crash the server due to config errors.
TypeScript Project Reloads
- Guard
project.reload()with a debounce; rapid reloads can starve the event loop. - Catch errors from TypeScript’s API and surface them with actionable messaging (e.g., “Failed to read tsconfig.json: …”).
Plugin Exceptions
- Wrap plugin hooks in
try/catchto prevent a single plugin from taking down the server.
function safeInvoke<T>(fn: () => T, fallback: T) {
try {
return fn();
} catch (err) {
log('error', 'Plugin error', { err: String(err) });
return fallback;
}
}
Memory Pressure
- Dispose snapshots (
snapshot.dispose?.()) when removing virtual files. - Avoid keeping entire project graphs in memory—rely on Volar’s internal caches.
- Expose a
volar/clearCachescommand for debugging sessions; reset caches when memory spikes.
Fallback Configuration Strategy
- Load default config.
- Merge user config (
server.configurations). - Validate; if validation fails, log error and keep defaults.
- Apply new config atomically (e.g., swap references to schema caches).
function applyConfiguration(rawConfig: unknown) {
const parsed = schema.safeParse(rawConfig);
if (!parsed.success) {
log('warn', 'Invalid config, using defaults', { issues: parsed.error.issues });
return defaultConfig;
}
return { ...defaultConfig, ...parsed.data };
}
Editor Recovery Patterns
VS Code
- Use
connection.languages.diagnostics.refreshafter recovering from an error (e.g., schema server returns to service) to prompt the client for fresh diagnostics. - Provide a command “Reload Volar Server” that restarts the process if the user wants a clean slate.
Neovim / CLI
- Document a
:VolarRestartcommand or CLI flag (volar --restart) to drop caches. - If the server exits due to an unrecoverable error, ensure the client auto-restarts it with limited retries to avoid infinite loops.
Testing Resilience
- Simulate network failures by mocking
fetchto throw errors. - Provide fixtures with malformed configuration and assert the server falls back to defaults without crashing.
- Force
server.documents.getto returnundefinedmid-request to confirm handlers bail out gracefully. - Run stress tests opening/closing hundreds of files to ensure watchers and caches don’t leak.
Monitoring & Alerts
- Emit telemetry for repeated failures (5 schema fetch failures in a row) and consider surfacing a notification prompting the user to check their network or configuration.
- Track server restarts if the host environment allows; multiple restarts in quick succession indicate a crash loop.
Quick Reference Checklist
- Cache schemas and support offline mode.
- Validate configuration and fall back to defaults on errors.
- Guard plugin hooks and log exceptions.
- Compare document versions before publishing diagnostics.
- Dispose snapshots and purge caches when documents close.
- Provide commands or RPC hooks to restart/clear caches.
- Surface actionable notifications for persistent issues.
- Cover failure scenarios in tests (network, config, missing files).
Designing for resilience upfront saves countless hours later. Treat every external dependency (network, file system, user config) as unreliable, and make fallbacks explicit so your language server remains stable under real-world conditions.