microsoft/typescript-go · warning
no file watcher exists with ID %s
Error message
no file watcher exists with ID %s
What it means
UnwatchFiles (server.go:276-280) with the builtin watcher active: the requested project.WatcherID is not in the server's registered set (s.watchers), so there is nothing to remove. It is a caller-state error — the id was never registered or was already unwatched, and the error names the offending id.
Source
Thrown at internal/lsp/server.go:279
Watchers: watchers,
},
},
},
},
})
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)View on GitHub (pinned to 1bcfa18d79)
Solutions
- Track watcher ids returned/created by your session layer and only unwatch ids you successfully registered (WatchFiles returned nil).
- Make cleanup idempotent in the caller: guard with your own 'registered' set or ignore this specific error during teardown.
- If hit on first unwatch, audit for a lost WatchFiles error that was swallowed earlier — registration may have failed and the id never entered s.watchers.
Example fix
// before
_ = client.UnwatchFiles(ctx, id)
// after: idempotent cleanup
if registered.Has(id) {
if err := client.UnwatchFiles(ctx, id); err != nil { return err }
registered.Delete(id)
} Defensive patterns
Strategy: validation
Validate before calling
// caller-side registry of live watcher ids
var liveWatchers collections.SyncSet[project.WatcherID]
func register(ctx context.Context, c project.Client, id project.WatcherID, ws []*lsproto.FileSystemWatcher) error {
if err := c.WatchFiles(ctx, id, ws); err != nil {
return err
}
liveWatchers.Add(id)
return nil
} Try / catch
if err := client.UnwatchFiles(ctx, id); err != nil {
if strings.HasPrefix(err.Error(), "no file watcher exists with ID") {
// already gone: benign during idempotent teardown
return nil
}
return err
} Prevention
- Record ids only after WatchFiles succeeds; WatchFiles adds to s.watchers only on success.
- Make teardown idempotent with a caller-side registry.
- Never synthesize WatcherIDs; use the ones issued at registration time.
When it happens
Trigger: Calling project.Client.UnwatchFiles with an id that was never passed to WatchFiles, calling UnwatchFiles twice for the same id (the first call deletes it from the set), or a session tracking bug that loses which watcher ids are live.
Common situations: Retry logic that re-runs cleanup and hits the second UnwatchFiles; multiple projects sharing one client and double-releasing watchers; tests constructing arbitrary WatcherIDs.
Related errors
- failed to register file watcher: %w
- failed to unregister file watcher: %w
- Invalid value for ${name}: ${value}
- forRelease requires setPrerelease unless nativePreviewReleas
- Language client is not initialized
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/b0435f00fe909ea1.
Report an issue: GitHub.