{"record":{"id":"f925f51c0c36a899","repo":"microsoft/typescript-go","slug":"cpu-profiling-not-in-progress","errorCode":null,"errorMessage":"CPU profiling not in progress","messagePattern":"CPU profiling not in progress","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/pprof/pprof.go","lineNumber":113,"sourceCode":"\t\tos.Remove(cpuProfilePath)\n\t\treturn fmt.Errorf(\"failed to start CPU profile: %w\", err)\n\t}\n\n\tc.session = &ProfileSession{\n\t\tcpuFilePath: cpuProfilePath,\n\t\tcpuFile:     cpuFile,\n\t\tlogWriter:   io.Discard,\n\t}\n\treturn nil\n}\n\n// StopCPUProfile stops CPU profiling and returns the path to the profile file.\nfunc (c *CPUProfiler) StopCPUProfile() (string, error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.session == nil {\n\t\treturn \"\", errors.New(\"CPU profiling not in progress\")\n\t}\n\n\tfilePath := c.session.cpuFilePath\n\tc.session.Stop()\n\tc.session = nil\n\n\treturn filePath, nil\n}\n\n// SaveHeapProfile saves a heap profile to the specified directory.\nfunc SaveHeapProfile(profileDir string) (string, error) {\n\tif err := os.MkdirAll(profileDir, 0o755); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create profile directory: %w\", err)\n\t}\n\n\theapProfilePath := filepath.Join(profileDir, fmt.Sprintf(\"%d-%d-heapprofile.pb.gz\", os.Getpid(), time.Now().UnixMilli()))\n\theapFile, err := os.Create(heapProfilePath)\n\tif err != nil {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/pprof/pprof.go#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Treat this error as benign if the goal is 'ensure stopped' — the state you wanted is already reached","Track profiling state client-side and only send stop after a confirmed start","Make teardown idempotent by ignoring this specific message"],"exampleFix":"// before\npath, err := profiler.StopCPUProfile()\nif err != nil { log.Fatal(err) }\n\n// after\npath, err := profiler.StopCPUProfile()\nif err != nil && err.Error() != \"CPU profiling not in progress\" {\n\treturn err // real failure\n}","handlingStrategy":"validation","validationCode":"// client-side state flag eliminates the mismatch\nif !profiling {\n\treturn \"\", nil // nothing to stop; desired state already holds\n}\npath, err := profiler.StopCPUProfile()\nprofiling = false","typeGuard":null,"tryCatchPattern":"path, err := profiler.StopCPUProfile()\nif err != nil {\n\tif err.Error() == \"CPU profiling not in progress\" {\n\t\treturn \"\", nil // idempotent stop: already stopped\n\t}\n\treturn \"\", err\n}","preventionTips":["Track start/stop state on the caller side","Make shutdown paths idempotent and tolerate the not-in-progress error","Do not blind-retry stop requests after timeouts without checking state"],"tags":["pprof","state","idempotency","profiling"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}