{"record":{"id":"e60bb135dbd0b389","repo":"microsoft/typescript-go","slug":"w-dir-is-required","errorCode":null,"errorMessage":"%w: dir is required","messagePattern":"%w: dir is required","errorType":"validation","errorClass":"ErrClientError","httpStatus":null,"severity":"error","filePath":"internal/api/session.go","lineNumber":871,"sourceCode":"\t\treturn s.handleStopCPUProfile(ctx)\n\tcase string(MethodSaveHeapProfile):\n\t\treturn s.handleSaveHeapProfile(ctx, parsed.(*ProfileParams))\n\tcase string(MethodGetReferencesToSymbolInFile):\n\t\treturn s.handleGetReferencesToSymbolInFile(ctx, parsed.(*GetReferencesToSymbolInFileParams))\n\tcase string(MethodGetReferencedSymbolsForNode):\n\t\treturn s.handleGetReferencedSymbolsForNode(ctx, parsed.(*GetReferencedSymbolsForNodeParams))\n\tcase string(MethodGetSignatureUsages):\n\t\treturn s.handleGetSignatureUsages(ctx, parsed.(*GetSignatureUsagesParams))\n\tcase string(MethodGetCompletionsAtPosition):\n\t\treturn s.handleGetCompletionsAtPosition(ctx, parsed.(*GetCompletionsAtPositionParams))\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown method: %s\", method)\n\t}\n}\n\nfunc (s *Session) handleStartCPUProfile(_ context.Context, params *ProfileParams) (any, error) {\n\tif params == nil || params.Dir == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: dir is required\", ErrClientError)\n\t}\n\tif err := s.cpuProfiler.StartCPUProfile(params.Dir); err != nil {\n\t\treturn nil, fmt.Errorf(\"%w: failed to start CPU profile: %w\", ErrClientError, err)\n\t}\n\treturn nil, nil\n}\n\nfunc (s *Session) handleStopCPUProfile(_ context.Context) (*ProfileResult, error) {\n\tfilePath, err := s.cpuProfiler.StopCPUProfile()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w: failed to stop CPU profile: %w\", ErrClientError, err)\n\t}\n\treturn &ProfileResult{File: filePath}, nil\n}\n\nfunc (s *Session) handleSaveHeapProfile(_ context.Context, params *ProfileParams) (*ProfileResult, error) {\n\tif params == nil || params.Dir == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: dir is required\", ErrClientError)","sourceCodeStart":853,"sourceCodeEnd":889,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/session.go#L853-L889","documentation":"startCPUProfile rejects params that are nil or have an empty Dir field. ProfileParams has a single string field dir; an omitted field decodes to the Go zero value \"\", which fails this check. The dir is where the server writes the pprof CPU profile file.","triggerScenarios":"Sending {} or null as params to startCPUProfile; omitting the dir key in the JSON payload; sending \"dir\": \"\" explicitly.","commonSituations":"Optional-looking params treated as skippable; client defaulting to empty string when a config knob (profile output directory) is unset.","solutions":["Pass a non-empty absolute directory path in the dir field","Default the value client-side (e.g. os.TempDir()) before sending when no explicit dir is configured"],"exampleFix":"// before\nconn.Call(ctx, string(api.MethodStartCPUProfile), api.ProfileParams{})\n\n// after\nconn.Call(ctx, string(api.MethodStartCPUProfile), api.ProfileParams{Dir: \"/tmp/tsprofiles\"})","handlingStrategy":"validation","validationCode":"func profileParams(dir string) (api.ProfileParams, error) {\n    if dir == \"\" { dir = os.TempDir() }\n    if !filepath.IsAbs(dir) { dir, _ = filepath.Abs(dir) }\n    return api.ProfileParams{Dir: dir}, nil\n}","typeGuard":"func hasProfileDir(p *api.ProfileParams) bool {\n    return p != nil && p.Dir != \"\"\n}","tryCatchPattern":null,"preventionTips":["Default the profile dir client-side (e.g. os.TempDir()) so it is never empty","Send absolute paths to avoid cwd ambiguity on the server"],"tags":["profiling","validation","cpu-profile","api"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}