JuliusBrussee/caveman · error
native runtime: session id is required
Error message
native runtime: session id is required
What it means
Runtime.Handle validates each request before processing; a request whose Session.ID is blank after trimming is rejected because all runtime state (decisions, CCR objects, receipts) is keyed by session. This check runs after protocol-version and event-type validation, under the runtime's mutex.
Source
Thrown at proxy/internal/nativeruntime/runtime.go:216
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()
r.mu.Lock()
defer r.mu.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
}
request.Profile = profile
r.lastActivity = time.Now()
if request.Event.Type == "session.end" {
delete(r.activeSessions, request.Session.ID)
} else {View on GitHub (pinned to 27d5a3981a)
Solutions
- Populate request.Session.ID with a stable non-empty session identifier before sending
- Check the client's JSON tags against the protocol's `session.id` path
- Allocate/persist a session id before the first Handle call and reuse it for the session's lifetime
Example fix
// before
resp, err := rt.Handle(ctx, nativeruntime.Request{Event: evt}) // Session.ID empty
// after
resp, err := rt.Handle(ctx, nativeruntime.Request{
Session: nativeruntime.Session{ID: sessionID},
Event: evt,
}) Defensive patterns
Strategy: validation
Validate before calling
if strings.TrimSpace(request.Session.ID) == "" {
return errors.New("allocate a session id before Handle")
}
resp, err := rt.Handle(ctx, request) Type guard
func hasSession(r nativeruntime.Request) bool {
return strings.TrimSpace(r.Session.ID) != ""
} Prevention
- Allocate and persist the session id before the first request
- Match client JSON field names to the protocol (session.id)
When it happens
Trigger: Sending a Request over the unix socket/named pipe with session.id omitted, empty, or whitespace; a client struct where the json tag for the session id doesn't match the protocol field name.
Common situations: First-request bootstrapping code that hasn't allocated a session yet; JSON field-name mismatch (sessionId vs session) in a hand-rolled client; tests constructing Request literals without Session.
Related errors
- ccr: typed object session_id is required
- native runtime: invalid decision id
- cachebench: nil corpus reader
- message exceeds byte limit
- tool message requires tool_call_id
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/73004008596034a7.
Report an issue: GitHub.