github/copilot-sdk · error

CliHash must be a SHA-256 hash

Error message

CliHash must be a SHA-256 hash (%d bytes), got %d bytes

What it means

embeddedcli.Setup requires CliHash to be exactly sha256.Size (32) bytes. If the provided hash has any other length, Setup panics. The hash is used to verify the embedded CLI binary's integrity at install time, so a wrong-length value means the checksum cannot be a valid SHA-256.

Solutions

  1. Decode a hex-encoded digest with hex.DecodeString before assigning CliHash
  2. Compute the hash with sha256.Sum256 so the slice is exactly 32 bytes
  3. Confirm you are passing the hash of the same bytes supplied as Cli
  4. Add a pre-call check: len(cliHash) != sha256.Size

Example fix

// before
hashHex := "abc123..." // 64-char hex string
Setup(Config{Cli: r, CliHash: []byte(hashHex)})
// after
raw, _ := hex.DecodeString(hashHex)
Setup(Config{Cli: r, CliHash: raw})
Defensive patterns

Strategy: validation

Validate before calling

if len(cliHash) != sha256.Size {
	return fmt.Errorf("CliHash must be %d bytes, got %d", sha256.Size, len(cliHash))
}

Prevention

When it happens

Trigger: Calling Setup with Config.CliHash set to a hex string (64 bytes... actually 64 chars/64 bytes if raw string), an empty slice, a truncated hash, or a SHA-1/MD5 digest of the wrong size.

Common situations: Passing hex-encoded hash strings instead of raw digest bytes (use hex.DecodeString first), copying hashes between fields of different assets, or generating the hash with the wrong algorithm.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

	LinuxMuslRuntimeLib            io.Reader
	LinuxMuslRuntimeLibHash        []byte
	LinuxMuslRuntimeExecutable     io.Reader
	LinuxMuslRuntimeExecutableHash []byte
	LinuxMuslRuntimeNode           io.Reader
	LinuxMuslRuntimeNodeHash       []byte
	LinuxMuslRuntimeAssets         io.Reader
	LinuxMuslRuntimeAssetsHash     []byte

	Dir     string
	Version string
}

func Setup(cfg Config) {
	if cfg.Cli == nil {
		panic("Cli reader is required")
	}
	if len(cfg.CliHash) != sha256.Size {
		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")

View on GitHub (pinned to cd8cf15dc3)