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{}, err

View on GitHub (pinned to df2ccd85c9)

Solutions

  1. 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.
  2. 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.
  3. Upgrade or downgrade the npm CLI package so its expected protocol version matches the runtime binary.
  4. 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

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


AI-assisted analysis of JuliusBrussee/caveman@df2ccd85c9 (2026-08-31). Data as JSON: /api/errors/48c8db0f6cda0187. Report an issue: GitHub.