microsoft/typescript-go · warning
failed to unregister file watcher: %w
Error message
failed to unregister file watcher: %w
What it means
UnwatchFiles (server.go:281-283) with the builtin watcher active: the id was registered, but the underlying lspwatcher backend failed to remove the OS-level watch; the error wraps the backend failure with %w. Set deletion is skipped on this path, so the id remains registered — the caller can retry.
Source
Thrown at internal/lsp/server.go:282
},
},
})
if err != nil {
return fmt.Errorf("failed to register file watcher: %w", err)
}
s.watchers.Add(id)
return nil
}
// UnwatchFiles implements project.Client.
func (s *Server) UnwatchFiles(ctx context.Context, id project.WatcherID) error {
if s.builtinWatcher != nil {
if !s.watchers.Has(id) {
return fmt.Errorf("no file watcher exists with ID %s", id)
}
if err := s.builtinWatcher.UnwatchFiles(string(id)); err != nil {
return fmt.Errorf("failed to unregister file watcher: %w", err)
}
s.watchers.Delete(id)
return nil
}
if s.watchers.Has(id) {
_, err := sendClientRequest(ctx, s, lsproto.ClientUnregisterCapabilityInfo, &lsproto.UnregistrationParams{
Unregisterations: []*lsproto.Unregistration{
{
Id: string(id),
Method: string(lsproto.MethodWorkspaceDidChangeWatchedFiles),
},
},
})
if err != nil {
return fmt.Errorf("failed to unregister file watcher: %w", err)
}
s.watchers.Delete(id)View on GitHub (pinned to 1bcfa18d79)
Solutions
- Retry UnwatchFiles once — because s.watchers still contains the id on this failure path, a retry is safe and often succeeds after transient backend errors.
- If the watched tree was deleted, the error is usually benign during teardown; log and continue.
- Check the wrapped backend error for actionable causes (handle invalid, path gone) before retrying.
Example fix
// before
if err := client.UnwatchFiles(ctx, id); err != nil { return err }
// after: tolerate failures for vanished paths during teardown
if err := client.UnwatchFiles(ctx, id); err != nil && !errors.Is(err, fs.ErrNotExist) {
log.Printf("unwatch %s: %v", id, err)
} Defensive patterns
Strategy: retry
Try / catch
if err := client.UnwatchFiles(ctx, id); err != nil {
if strings.Contains(err.Error(), "failed to unregister file watcher") {
// id remains registered on this path; one retry is safe
if err2 := client.UnwatchFiles(ctx, id); err2 != nil {
log.Printf("unwatch %s failed twice: %v / %v", id, err, err2)
}
} else {
return err
}
} Prevention
- On unregister failure the id stays in s.watchers, so retries are meaningful — build cleanup to allow one retry.
- During teardown after directory deletion, log and continue rather than propagating.
- Inspect the wrapped backend error before deciding to retry.
When it happens
Trigger: The OS watch handle is already gone (directory deleted), backend close errors, or a backend-specific failure while unwatching the pattern set registered under that id string.
Common situations: Projects whose watched directories are deleted or unmounted before cleanup; shutdown races where the watcher is closing concurrently; resource exhaustion making teardown fail.
Related errors
- failed to register file watcher: %w
- no file watcher exists with ID %s
- Invalid value for ${name}: ${value}
- forRelease requires setPrerelease unless nativePreviewReleas
- No members found for enum ${def.name} in ${def.goFile}
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/2d1a5876973b8857.
Report an issue: GitHub.