github/copilot-sdk · error
%sRuntimeExecutableHash must be a SHA-256 hash
Error message
%sRuntimeExecutableHash must be a SHA-256 hash (%d bytes), got %d bytes
What it means
After confirming both members of a runtime pair are present, validateRuntimePairConfig checks that the wrapper executable's hash is exactly 32 bytes (SHA-256 size). A hash of any other length cannot be used by the verify-on-install pipeline, so Setup panics with the platform prefix (e.g. "LinuxMuslRuntimeExecutableHash") embedded in the message. This fail-fast check precedes any file writes.
Solutions
- Decode the hex digest to 32 raw bytes with hex.DecodeString before assigning it to the hash field.
- Recompute it directly: h := sha256.Sum256(wrapperBytes); cfg.LinuxMuslRuntimeExecutableHash = h[:].
- Verify the digest source produces raw SHA-256 (32 bytes), not base64/hex text or a different algorithm's output.
Example fix
// before
cfg.LinuxMuslRuntimeExecutableHash = []byte(hashHex) // 64 bytes
// after
raw, err := hex.DecodeString(hashHex)
if err != nil || len(raw) != sha256.Size { /* fix pipeline */ }
cfg.LinuxMuslRuntimeExecutableHash = raw Defensive patterns
Strategy: validation
Validate before calling
func validWrapperHash(h []byte) bool { return len(h) == sha256.Size } Type guard
func isSHA256(b []byte) bool { return len(b) == sha256.Size } Try / catch
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "RuntimeExecutableHash must be a SHA-256 hash") {
log.Fatalf("bad runtime executable hash: %s", s)
}
panic(r)
}
}() Prevention
- Always hex.DecodeString digest strings before assigning to hash fields.
- Assert digest length right after generation in the build pipeline.
- Keep a shared helper that produces ([32]byte, error) digests so the type enforces the length.
When it happens
Trigger: Calling Setup where cfg.LinuxMuslRuntimeExecutableHash (or the default RuntimeExecutableHash) is a hex string's bytes, an empty slice, or a non-SHA-256 digest while the corresponding executable and node are both set.
Common situations: Storing hashes as hex strings and forgetting hex.DecodeString; using a SHA-1 or truncated digest from an older pipeline; copy-pasting a hash across platforms with mismatched length.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- must be a SHA-256 hash ( bytes), got bytes
- %sRuntimeNodeHash must be a SHA-256 hash
- checksum mismatch for
- CliHash must be a SHA-256 hash
- %sRuntimeExecutable and %sRuntimeNode must be provided…
AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09).
Data as JSON: /api/errors/59696deb731f0046.
Report an issue: GitHub.
Appendix: source
Thrown at go/internal/embeddedcli/embeddedcli.go:425
hash := sha256.Sum256(content)
mode := os.FileMode(header.Mode & 0777)
if err := installVerifiedFile(path, bytes.NewReader(content), hash[:], mode, "runtime asset"); err != nil {
return err
}
}
runtimeAssetsInstalled = true
return nil
}
func validateRuntimePairConfig(wrapper io.Reader, wrapperHash []byte, node io.Reader, nodeHash []byte, prefix string) {
if (wrapper == nil) != (node == nil) {
panic(prefix + "RuntimeExecutable and " + prefix + "RuntimeNode must be provided together")
}
if wrapper == nil {
return
}
if len(wrapperHash) != sha256.Size {
panic(fmt.Sprintf("%sRuntimeExecutableHash must be a SHA-256 hash (%d bytes), got %d bytes", prefix, sha256.Size, len(wrapperHash)))
}
if len(nodeHash) != sha256.Size {
panic(fmt.Sprintf("%sRuntimeNodeHash must be a SHA-256 hash (%d bytes), got %d bytes", prefix, sha256.Size, len(nodeHash)))
}
}
func installRuntimePair(installDir string) (string, error) {
nodePath := filepath.Join(installDir, "runtime.node")
if err := installVerifiedFile(nodePath, config.RuntimeNode, config.RuntimeNodeHash, 0644, "runtime.node"); err != nil {
return "", err
}
wrapperPath := filepath.Join(installDir, runtimeExecutableName())
if err := installVerifiedFile(wrapperPath, config.RuntimeExecutable, config.RuntimeExecutableHash, 0755, "runtime wrapper"); err != nil {
return "", err
}
return wrapperPath, nil
}
View on GitHub (pinned to cd8cf15dc3)