JuliusBrussee/caveman · error · error
native runtime: unsupported protocol version %d
Error message
native runtime: unsupported protocol version %d
What it means
Handle validates every inbound Request before doing any work; the very first check compares request.ProtocolVersion against the runtime's exported ProtocolVersion constant. If the client speaks a different wire-protocol version than this native runtime binary, Handle refuses the request with this error instead of misinterpreting the payload. It is a hard compatibility gate between the caller (serveConn) and the runtime process.
Source
Thrown at proxy/internal/nativeruntime/runtime.go:212
case "ccr-masking":
return profile, profileFeatures{capture: true, mask: true}, nil
case "cache-aware":
return profile, profileFeatures{taskContract: true, compactState: true}, nil
case "full-safe", "full-max":
return profile, profileFeatures{taskContract: true, compactState: true, reuse: true, capture: true, mask: true, repository: true}, nil
default:
return "", profileFeatures{}, fmt.Errorf("native runtime: unknown profile %q", raw)
}
}
func (r *Runtime) Handle(_ context.Context, request Request) (Response, error) {
started := time.Now()
sessionLock := r.sessionLock(request.Session.ID)
sessionLock.Lock()
defer sessionLock.Unlock()
if request.ProtocolVersion != ProtocolVersion {
return Response{}, fmt.Errorf("native runtime: unsupported protocol version %d", request.ProtocolVersion)
}
if _, ok := eventTypes[request.Event.Type]; !ok {
return Response{}, fmt.Errorf("native runtime: unknown event %q", request.Event.Type)
}
if strings.TrimSpace(request.Session.ID) == "" {
return Response{}, errors.New("native runtime: session id is required")
}
policyMode := request.PolicyMode
if policyMode == "" {
policyMode = "safe"
}
if policyMode != "record" && policyMode != "safe" && policyMode != "max" {
return Response{}, fmt.Errorf("native runtime: unknown policy mode %q", policyMode)
}
request.PolicyMode = policyMode
profile, features, err := resolveProfile(request.Profile, policyMode)
if err != nil {
return Response{}, errView on GitHub (pinned to df2ccd85c9)
Solutions
- Rebuild/reinstall the local Go binaries so both sides use the same protocol: run scripts/install-local-cli.sh (or install-local-cli.ps1) from the repo root.
- Check for stray CAVEMAN_PROXY_BIN/CAVEMAN_*_BIN env overrides pointing at an outdated binary and remove or update them; run `caveman setup` to see per-binary status.
- Upgrade or downgrade the npm CLI package so its expected protocol version matches the runtime binary.
- If you own the client code, set request.ProtocolVersion to the runtime's exported ProtocolVersion constant instead of a literal.
Example fix
// before: client hardcodes an old protocol version
request := nativeruntime.Request{ProtocolVersion: 1, Session: session, Event: event}
// after: always use the constant from the same package version
request := nativeruntime.Request{ProtocolVersion: nativeruntime.ProtocolVersion, Session: session, Event: event} Defensive patterns
Strategy: validation
Validate before calling
// Before sending, confirm client and runtime agree on the protocol version.
function validateRuntimeCompat(clientVersion: number, runtimeVersion: number): void {
if (clientVersion !== runtimeVersion) {
throw new Error(
`runtime protocol mismatch: client=${clientVersion} runtime=${runtimeVersion}; ` +
"rebuild Go binaries (scripts/install-local-cli.sh) or update the CLI",
);
}
}
validateRuntimeCompat(nativeruntime.ProtocolVersion, runtime.ProtocolVersion); Type guard
func supportsProtocol(request Request) bool {
return request.ProtocolVersion == ProtocolVersion
} Try / catch
resp, err := runtime.Handle(ctx, request)
if err != nil {
var unsupported * UnsupportedProtocolError
if errors.Is(err, ErrUnsupportedProtocol) || strings.Contains(err.Error(), "unsupported protocol version") {
log.Fatalf("runtime/CLI version skew: %v — run scripts/install-local-cli.sh", err)
}
return err
} Prevention
- Rebuild all local Go binaries after every CLI upgrade (scripts/install-local-cli.sh / .ps1); run `caveman setup` to confirm each binary resolves.
- Audit CAVEMAN_*_BIN env overrides — stale paths to old binaries are the top cause of version skew.
- Always reference nativeruntime.ProtocolVersion in client code; never hardcode a numeric version.
- Pin CLI package and local binary builds to the same release when deploying across machines.
When it happens
Trigger: Any call to Handle (via serveConn) where request.ProtocolVersion != ProtocolVersion, e.g. a client built against protocol v1 sending to a runtime expecting v2, a stale ~/.caveman/bin runtime binary paired with a newer CLI, or hand-rolled IPC clients hardcoding an old version number.
Common situations: Upgrading the npm CLI (`caveman`) without rebuilding the local Go binaries via scripts/install-local-cli.sh (or vice versa); CAVEMAN_PROXY_BIN / CAVEMAN_*_BIN env overrides pointing at an old or third-party binary; mixing versions across machines with a shared ~/.caveman directory.
Related errors
- option not found
- cave_harness_upstream_version_mismatch
- rewriter: model is required
- rewriter: api key is required
- kms: auth token is required
AI-assisted analysis of JuliusBrussee/caveman@df2ccd85c9 (2026-08-31).
Data as JSON: /api/errors/48c8db0f6cda0187.
Report an issue: GitHub.