github/copilot-sdk · error
%sRuntimeNodeHash must be a SHA-256 hash
Error message
%sRuntimeNodeHash must be a SHA-256 hash (%d bytes), got %d bytes
What it means
validateRuntimePairConfig performs the same 32-byte SHA-256 length check for the runtime node library's hash as it does for the wrapper: when both pair members are set but the node hash has the wrong length, Setup panics naming the platform prefix (e.g. "LinuxMuslRuntimeNodeHash"). This guarantees installRuntimePair can verify runtime.node against its digest after writing it.
Solutions
- Convert the hex digest with hex.DecodeString so the field holds exactly 32 bytes.
- Recompute: h := sha256.Sum256(nodeBytes); cfg.LinuxMuslRuntimeNodeHash = h[:].
- Trim whitespace/newlines from the digest string before decoding; assert len(raw) == sha256.Size in the build step.
Example fix
// before cfg.LinuxMuslRuntimeNodeHash = []byte(strings.TrimSpace(digestHex)) // 64 bytes // after raw, _ := hex.DecodeString(strings.TrimSpace(digestHex)) cfg.LinuxMuslRuntimeNodeHash = raw // 32 bytes
Defensive patterns
Strategy: validation
Validate before calling
func validNodeHash(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, "RuntimeNodeHash must be a SHA-256 hash") {
log.Fatalf("bad runtime node hash: %s", s)
}
panic(r)
}
}() Prevention
- Decode hex digests to 32 raw bytes before storing them in Config.
- Trim whitespace from digests produced by shell/CI steps before decoding.
- Validate all hash fields (executable and node) in a pre-Setup sanity test.
When it happens
Trigger: Calling Setup with the runtime pair present but cfg.LinuxMuslRuntimeNodeHash (or the default RuntimeNodeHash) empty, hex-encoded, or otherwise not exactly 32 raw bytes.
Common situations: Hash generated by a different tool (base64 or hex text); field copied from the executable hash but truncated/emptied during a refactor; CI step that emits the digest with a newline-included string converted to bytes.
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
- %sRuntimeExecutableHash 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/1a92b03ae43b7cdc.
Report an issue: GitHub.
Appendix: source
Thrown at go/internal/embeddedcli/embeddedcli.go:428
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
}
func installVerifiedFile(path string, reader io.Reader, expectedHash []byte, mode os.FileMode, label string) error {
if _, err := os.Stat(path); err == nil {
existingHash, err := hashFile(path)View on GitHub (pinned to cd8cf15dc3)