{"record":{"id":"b53c9f3f895eee70","repo":"github/copilot-sdk","slug":"setup-must-be-called-before-path-is-accessed","errorCode":null,"errorMessage":"Setup must be called before Path is accessed","messagePattern":"Setup must be called before Path is accessed","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/internal/embeddedcli/embeddedcli.go","lineNumber":88,"sourceCode":"\t\tpanic(fmt.Sprintf(\"CliHash must be a SHA-256 hash (%d bytes), got %d bytes\", sha256.Size, len(cfg.CliHash)))\n\t}\n\tif cfg.LinuxMuslCli != nil && len(cfg.LinuxMuslCliHash) != sha256.Size {\n\t\tpanic(fmt.Sprintf(\"LinuxMuslCliHash must be a SHA-256 hash (%d bytes), got %d bytes\", sha256.Size, len(cfg.LinuxMuslCliHash)))\n\t}\n\tif cfg.LinuxMuslRuntimeLib != nil && len(cfg.LinuxMuslRuntimeLibHash) != sha256.Size {\n\t\tpanic(fmt.Sprintf(\"LinuxMuslRuntimeLibHash must be a SHA-256 hash (%d bytes), got %d bytes\", sha256.Size, len(cfg.LinuxMuslRuntimeLibHash)))\n\t}\n\tvalidateRuntimePairConfig(cfg.RuntimeExecutable, cfg.RuntimeExecutableHash, cfg.RuntimeNode, cfg.RuntimeNodeHash, \"\")\n\tvalidateRuntimePairConfig(cfg.LinuxMuslRuntimeExecutable, cfg.LinuxMuslRuntimeExecutableHash, cfg.LinuxMuslRuntimeNode, cfg.LinuxMuslRuntimeNodeHash, \"LinuxMusl\")\n\tvalidateOptionalHash(cfg.RuntimeAssets, cfg.RuntimeAssetsHash, \"RuntimeAssetsHash\")\n\tvalidateOptionalHash(cfg.LinuxMuslRuntimeAssets, cfg.LinuxMuslRuntimeAssetsHash, \"LinuxMuslRuntimeAssetsHash\")\n\tsetupMu.Lock()\n\tdefer setupMu.Unlock()\n\tif setupDone {\n\t\tpanic(\"Setup must only be called once\")\n\t}\n\tif pathInitialized {\n\t\tpanic(\"Setup must be called before Path is accessed\")\n\t}\n\tconfig = cfg\n\tsetupDone = true\n}\n\nvar Path = sync.OnceValue(func() string {\n\tsetupMu.Lock()\n\tdefer setupMu.Unlock()\n\tif !setupDone {\n\t\treturn \"\"\n\t}\n\tpathInitialized = true\n\tpath := install()\n\treturn path\n})\n\n// RuntimeLibPath returns the on-disk path to the installed native in-process\n// runtime library (cdylib), or \"\" when no runtime library was bundled or the","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/go/internal/embeddedcli/embeddedcli.go#L70-L106","documentation":"Setup() refuses to run if Path() has already been accessed (pathInitialized is set by the lazily-initialized sync.OnceValue Path variable). Accessing Path before Setup would trigger installation with a nil config, so the library panics to enforce the documented order: Setup must run before any Path read. The panic preserves a deterministic one-time initialization sequence.","triggerScenarios":"Any read of embeddedcli.Path (directly, or via Path() call) occurring before embeddedcli.Setup(cfg) has completed — e.g. Path referenced in another package's init(), a package-level var initialized at load time, or Path accessed in a test before the Setup helper runs.","commonSituations":"Package-level vars or init() functions that resolve the CLI path at startup; goroutines started before Setup that read Path; test ordering where one test touches Path before the Setup test runs.","solutions":["Call embeddedcli.Setup(cfg) as the very first operation in main() (or TestMain) before any code path can touch Path.","Remove package-level initialization that reads Path at import time; defer it until after Setup inside a function.","Access Path only through a helper that itself ensures Setup ran first (e.g. wrap both in one sync.Once)."],"exampleFix":"// before\nvar cliPath = embeddedcli.Path() // runs at init, before Setup\n\n// after\nvar cliPathOnce sync.Once\nvar cliPathValue string\nfunc cliPath() string {\n    cliPathOnce.Do(func() {\n        ensureSetup()\n        cliPathValue = embeddedcli.Path()\n    })\n    return cliPathValue\n}","handlingStrategy":"try-catch","validationCode":"func ensureSetupBeforePath() {\n    if !setupCompleted { // your own flag set after Setup returns\n        ensureSetup()\n    }\n    _ = embeddedcli.Path()\n}","typeGuard":"func pathReady() bool { return setupCompleted }","tryCatchPattern":"func getPath() (p string) {\n    defer func() {\n        if r := recover(); r != nil {\n            if strings.Contains(fmt.Sprint(r), \"Setup must be called before Path\") {\n                ensureSetup()\n                p = embeddedcli.Path()\n                return\n            }\n            panic(r)\n        }\n    }()\n    return embeddedcli.Path()\n}","preventionTips":["Call Setup first thing in main()/TestMain before spawning goroutines or initializing package vars.","Never read Path in init() functions or package-level variable initializers.","Centralize Path access behind a helper that guarantees Setup ran."],"tags":["go","panic","initialization-order","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}