microsoft/typescript-go · warning

CPU profiling not in progress

Error message

CPU profiling not in progress

What it means

StopCPUProfile found c.session == nil, meaning no CPU profile was started through this CPUProfiler. There is nothing to flush and no path to return, so the stop request is rejected instead of silently succeeding. This mirrors the start/stop pairing contract of the profiling API.

Source

Thrown at internal/pprof/pprof.go:113

		os.Remove(cpuProfilePath)
		return fmt.Errorf("failed to start CPU profile: %w", err)
	}

	c.session = &ProfileSession{
		cpuFilePath: cpuProfilePath,
		cpuFile:     cpuFile,
		logWriter:   io.Discard,
	}
	return nil
}

// StopCPUProfile stops CPU profiling and returns the path to the profile file.
func (c *CPUProfiler) StopCPUProfile() (string, error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.session == nil {
		return "", errors.New("CPU profiling not in progress")
	}

	filePath := c.session.cpuFilePath
	c.session.Stop()
	c.session = nil

	return filePath, nil
}

// SaveHeapProfile saves a heap profile to the specified directory.
func SaveHeapProfile(profileDir string) (string, error) {
	if err := os.MkdirAll(profileDir, 0o755); err != nil {
		return "", fmt.Errorf("failed to create profile directory: %w", err)
	}

	heapProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-%d-heapprofile.pb.gz", os.Getpid(), time.Now().UnixMilli()))
	heapFile, err := os.Create(heapProfilePath)
	if err != nil {

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Treat this error as benign if the goal is 'ensure stopped' — the state you wanted is already reached
  2. Track profiling state client-side and only send stop after a confirmed start
  3. Make teardown idempotent by ignoring this specific message

Example fix

// before
path, err := profiler.StopCPUProfile()
if err != nil { log.Fatal(err) }

// after
path, err := profiler.StopCPUProfile()
if err != nil && err.Error() != "CPU profiling not in progress" {
	return err // real failure
}
Defensive patterns

Strategy: validation

Validate before calling

// client-side state flag eliminates the mismatch
if !profiling {
	return "", nil // nothing to stop; desired state already holds
}
path, err := profiler.StopCPUProfile()
profiling = false

Try / catch

path, err := profiler.StopCPUProfile()
if err != nil {
	if err.Error() == "CPU profiling not in progress" {
		return "", nil // idempotent stop: already stopped
	}
	return "", err
}

Prevention

When it happens

Trigger: Calling stop twice (first stop already nilled the session); calling stop after a failed start; stop racing a start that never happened because the client lost track of state; stop issued after the runtime profile was started by a different component not tracked here.

Common situations: Client retry logic duplicating the stop request after a timeout; UI showing a 'profiling' state that desynced from the server after an error; scripts that unconditionally stop at exit.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/f925f51c0c36a899. Report an issue: GitHub.