microsoft/typescript-go · error · ErrClientError
%w: failed to save heap profile: %w
Error message
%w: failed to save heap profile: %w
What it means
saveHeapProfile forwarded the error from pprof.SaveHeapProfile, wrapped in ErrClientError. SaveHeapProfile creates and writes a file under dir, so failures are file-creation or write failures: missing directory, permission denied, read-only filesystem, or disk full.
Source
Thrown at internal/api/session.go:893
}
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 handling
return nil
}
func (s *Session) handleInitialize(ctx context.Context) (*InitializeResponse, error) {
return &InitializeResponse{
UseCaseSensitiveFileNames: s.projectSession.FS().UseCaseSensitiveFileNames(),
CurrentDirectory: s.projectSession.GetCurrentDirectory(),
}, nil
}
// handleUpdateSnapshot creates a new snapshot, optionally opening or closingView on GitHub (pinned to 1bcfa18d79)
Solutions
- Create dir and verify writability before issuing the request (write a probe file from the server's user)
- Free disk space or choose a volume with headroom comparable to server memory
- Use an absolute path to avoid cwd-relative surprises
Example fix
// before
saveHeapProfile("profiles") // relative, may not exist where the server resolves it
// after
os.MkdirAll("/data/tsprofiles", 0o755)
saveHeapProfile("/data/tsprofiles") Defensive patterns
Strategy: validation
Validate before calling
func ensureWritableDir(dir string) error {
if err := os.MkdirAll(dir, 0o755); err != nil { return err }
probe := filepath.Join(dir, ".probe")
if err := os.WriteFile(probe, nil, 0o644); err != nil { return err }
return os.Remove(probe)
}
// Call ensureWritableDir(params.Dir) from the server's user context before saveHeapProfile. Try / catch
if _, err := session.HandleRequest(ctx, string(api.MethodSaveHeapProfile), params); err != nil {
if strings.Contains(err.Error(), "heap profile") {
// dir problem or disk full: check space and permissions, then retry once after fixing
}
return err
} Prevention
- Pre-create and probe-writable the profile dir before requesting heap dumps
- Ensure free disk at least the size of server RSS before heap profiling
- Use an absolute path on a writable volume, not the container's read-only layer
When it happens
Trigger: Passing a dir that does not exist; server process lacking write permission to dir; disk exhaustion during the heap dump (heap profiles of large compile servers can be hundreds of MB); path pointing at a file instead of a directory.
Common situations: Profiling production containers with read-only layers; dirs created in a different mount namespace; disk pressure on CI runners.
Related errors
- %w: failed to start CPU profile: %w
- %w: dir is required
- %w: failed to stop CPU profile: %w
- %w: failed to update snapshot: %w
- %w: could not read file %q
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/dbec9cc6cf5c4abb.
Report an issue: GitHub.