microsoft/typescript-go · error

failed to start CPU profile: %w

Error message

failed to start CPU profile: %w

What it means

runtime/pprof.StartCPUProfile refused to begin; the overwhelmingly common cause is its 'CPU profiling already in progress' error because the Go runtime allows exactly one active CPU profile per process. This can fire even though this CPUProfiler has no session — another component in the same process called runtime/pprof directly (e.g. net/http/pprof handler, test flags, an embedded library). The file is closed and removed on failure, so no residue is left.

Source

Thrown at internal/pprof/pprof.go:96

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

	if err := os.MkdirAll(profileDir, 0o755); err != nil {
		return fmt.Errorf("failed to create profile directory: %w", err)
	}

	cpuProfilePath := filepath.Join(profileDir, fmt.Sprintf("%d-%d-cpuprofile.pb.gz", os.Getpid(), time.Now().UnixMilli()))
	cpuFile, err := os.Create(cpuProfilePath)
	if err != nil {
		return fmt.Errorf("failed to create CPU profile file: %w", err)
	}

	if err := pprof.StartCPUProfile(cpuFile); err != nil {
		cpuFile.Close()
		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")
	}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Stop the other profiling source first (close the /debug/pprof/profile request, remove -cpuprofile, stop the other library's session)
  2. Check the wrapped error text: 'CPU profiling already in progress' confirms the single-slot conflict
  3. Design tooling so only one profiler front-end owns runtime/pprof per process
Defensive patterns

Strategy: validation

Validate before calling

// ensure no other runtime/pprof consumer is active before starting
// (net/http/pprof, go test -cpuprofile, embedded libs)
if otherProfilerActive() {
	return errors.New("stop the other CPU profiler first")
}

Try / catch

if err := profiler.StartCPUProfile(dir); err != nil {
	if strings.Contains(err.Error(), "failed to start CPU profile") {
		// single-slot runtime conflict: stop the other source, then retry once
		stopExternalProfilers()
		return profiler.StartCPUProfile(dir)
	}
	return err
}

Prevention

When it happens

Trigger: net/http/pprof's /debug/pprof/profile endpoint active and capturing; go test -cpuprofile running the binary; a co-embedded library starting its own profile; leftover profile started through a previous API path not tracked by this struct.

Common situations: Diagnosis tooling stacked on the same process (prometheus + pprof endpoints + this API); CI running binaries under test coverage with profiling flags; duplicated profiling wrappers from merged codebases.

Related errors


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