microsoft/typescript-go · error · ErrClientError
%w: failed to stop CPU profile: %w
Error message
%w: failed to stop CPU profile: %w
What it means
stopCPUProfile forwarded the profiler error, wrapped in ErrClientError. The typical cause is stopping when no CPU profile is active (the start failed earlier or was never issued), or an error finalizing/flushing the profile file when profiling stops.
Source
Thrown at internal/api/session.go:882
default:
return nil, fmt.Errorf("unknown method: %s", method)
}
}
func (s *Session) handleStartCPUProfile(_ context.Context, params *ProfileParams) (any, error) {
if params == nil || params.Dir == "" {
return nil, fmt.Errorf("%w: dir is required", ErrClientError)
}
if err := s.cpuProfiler.StartCPUProfile(params.Dir); err != nil {
return nil, fmt.Errorf("%w: failed to start CPU profile: %w", ErrClientError, err)
}
return nil, nil
}
func (s *Session) handleStopCPUProfile(_ context.Context) (*ProfileResult, error) {
filePath, err := s.cpuProfiler.StopCPUProfile()
if err != nil {
return nil, fmt.Errorf("%w: failed to stop CPU profile: %w", ErrClientError, err)
}
return &ProfileResult{File: filePath}, nil
}
func (s *Session) handleSaveHeapProfile(_ context.Context, params *ProfileParams) (*ProfileResult, error) {
if params == nil || params.Dir == "" {
return nil, fmt.Errorf("%w: dir is required", ErrClientError)
}
filePath, err := pprof.SaveHeapProfile(params.Dir)
if err != nil {
return nil, fmt.Errorf("%w: failed to save heap profile: %w", ErrClientError, err)
}
return &ProfileResult{File: filePath}, nil
}
// HandleNotification implements Handler.
func (s *Session) HandleNotification(ctx context.Context, method string, params json.Value) error {
// TODO: Implement notification handlingView on GitHub (pinned to 1bcfa18d79)
Solutions
- Only call stopCPUProfile after a startCPUProfile that returned success
- Track start/stop state client-side and reset it on any error
- Treat a stop error as non-fatal during shutdown; capture the file path only on success
Example fix
// before
startCPUProfile(dir) // error ignored
stopCPUProfile() // "failed to stop"
// after
if err := startCPUProfile(dir); err != nil { return err }
res, err := stopCPUProfile() // only reached when start succeeded Defensive patterns
Strategy: validation
Validate before calling
// Track profiler state; only stop what you successfully started.
var profiling atomic.Bool
if err := startCPUProfile(dir); err != nil { return err }
profiling.Store(true)
defer func() {
if profiling.CompareAndSwap(true, false) {
_, _ = stopCPUProfile()
}
}() Type guard
func shouldStopCPU(profiling bool) bool { return profiling } Try / catch
if _, err := session.HandleRequest(ctx, string(api.MethodStopCPUProfile), nil); err != nil {
// Most common: no active profile. Treat as non-fatal during shutdown; log and continue.
log.Printf("stopCPUProfile: %v", err)
} Prevention
- Reset profiling state whenever startCPUProfile returns an error
- Make shutdown stop handlers idempotent so double-stop cannot occur
- Do not treat stop failures as fatal; the profile file may already be usable
When it happens
Trigger: Calling stopCPUProfile after a startCPUProfile that returned an error; calling stop twice; disk/full-permission problems flushing the profile on stop.
Common situations: Error from start swallowed by client code, so the caller believes a profile is running; shutdown hooks that unconditionally stop profiling; profiling guards implemented with bare flags that drift from real profiler state.
Related errors
- %w: dir is required
- %w: failed to start CPU profile: %w
- Cannot run a temporary file update on an inactive snapshot
- Snapshot is disposed
- Cannot run a temporary file update on an inactive snapshot
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/bac87c1de4f6d2ff.
Report an issue: GitHub.