github/copilot-sdk · error

Setup must be called before Path is accessed

Error message

Setup must be called before Path is accessed

What it means

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.

Solutions

  1. Call embeddedcli.Setup(cfg) as the very first operation in main() (or TestMain) before any code path can touch Path.
  2. Remove package-level initialization that reads Path at import time; defer it until after Setup inside a function.
  3. Access Path only through a helper that itself ensures Setup ran first (e.g. wrap both in one sync.Once).

Example fix

// before
var cliPath = embeddedcli.Path() // runs at init, before Setup

// after
var cliPathOnce sync.Once
var cliPathValue string
func cliPath() string {
    cliPathOnce.Do(func() {
        ensureSetup()
        cliPathValue = embeddedcli.Path()
    })
    return cliPathValue
}
Defensive patterns

Strategy: try-catch

Validate before calling

func ensureSetupBeforePath() {
    if !setupCompleted { // your own flag set after Setup returns
        ensureSetup()
    }
    _ = embeddedcli.Path()
}

Type guard

func pathReady() bool { return setupCompleted }

Try / catch

func getPath() (p string) {
    defer func() {
        if r := recover(); r != nil {
            if strings.Contains(fmt.Sprint(r), "Setup must be called before Path") {
                ensureSetup()
                p = embeddedcli.Path()
                return
            }
            panic(r)
        }
    }()
    return embeddedcli.Path()
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/b53c9f3f895eee70. Report an issue: GitHub.

Appendix: source

Thrown at go/internal/embeddedcli/embeddedcli.go:88

		panic(fmt.Sprintf("CliHash must be a SHA-256 hash (%d bytes), got %d bytes", sha256.Size, len(cfg.CliHash)))
	}
	if cfg.LinuxMuslCli != nil && len(cfg.LinuxMuslCliHash) != sha256.Size {
		panic(fmt.Sprintf("LinuxMuslCliHash must be a SHA-256 hash (%d bytes), got %d bytes", sha256.Size, len(cfg.LinuxMuslCliHash)))
	}
	if cfg.LinuxMuslRuntimeLib != nil && len(cfg.LinuxMuslRuntimeLibHash) != sha256.Size {
		panic(fmt.Sprintf("LinuxMuslRuntimeLibHash must be a SHA-256 hash (%d bytes), got %d bytes", sha256.Size, len(cfg.LinuxMuslRuntimeLibHash)))
	}
	validateRuntimePairConfig(cfg.RuntimeExecutable, cfg.RuntimeExecutableHash, cfg.RuntimeNode, cfg.RuntimeNodeHash, "")
	validateRuntimePairConfig(cfg.LinuxMuslRuntimeExecutable, cfg.LinuxMuslRuntimeExecutableHash, cfg.LinuxMuslRuntimeNode, cfg.LinuxMuslRuntimeNodeHash, "LinuxMusl")
	validateOptionalHash(cfg.RuntimeAssets, cfg.RuntimeAssetsHash, "RuntimeAssetsHash")
	validateOptionalHash(cfg.LinuxMuslRuntimeAssets, cfg.LinuxMuslRuntimeAssetsHash, "LinuxMuslRuntimeAssetsHash")
	setupMu.Lock()
	defer setupMu.Unlock()
	if setupDone {
		panic("Setup must only be called once")
	}
	if pathInitialized {
		panic("Setup must be called before Path is accessed")
	}
	config = cfg
	setupDone = true
}

var Path = sync.OnceValue(func() string {
	setupMu.Lock()
	defer setupMu.Unlock()
	if !setupDone {
		return ""
	}
	pathInitialized = true
	path := install()
	return path
})

// RuntimeLibPath returns the on-disk path to the installed native in-process
// runtime library (cdylib), or "" when no runtime library was bundled or the

View on GitHub (pinned to cd8cf15dc3)