JuliusBrussee/caveman · error
native session key length =
Error message
native session key length = %d, want %d
What it means
An existing session.key does not contain exactly 32 bytes, so it cannot be the HMAC key this runtime writes; the message reports both the actual and expected lengths. The size mismatch means the file was created or altered by something other than this code: manual editing, a truncated copy or restore, a different tool version's format, or an empty file from touch. The runtime refuses to guess rather than silently change the MAC domain.
Solutions
- If no session markers need to survive: rm <home>/runtime/session.key and restart — a fresh key is minted and old signed markers become inert text
- If you have the correct key material, restore exactly 32 bytes (re-copy the original file, do not retype it)
- Verify: wc -c <home>/runtime/session.key must print 32
- Audit what wrote the file (shell history, sync tools) so it does not recur
Example fix
# before $ wc -c ~/.caveman/runtime/session.key 12 $ caveman start error: native session key length = 12, want 32 # after $ rm ~/.caveman/runtime/session.key $ caveman start # mints a fresh 32-byte key ok
Defensive patterns
Strategy: validation
Validate before calling
keyPath := filepath.Join(home, "runtime", "session.key")
if info, err := os.Stat(keyPath); err == nil && info.Size() != 32 {
return fmt.Errorf("session.key is %d bytes (want 32) — remove %s to mint a fresh key (old markers become inert)", info.Size(), keyPath)
}
// safe to call nativeruntime.LoadOrCreateSessionKey(home) Try / catch
key, err := nativeruntime.LoadOrCreateSessionKey(home)
if err != nil {
if strings.HasPrefix(err.Error(), "native session key length") {
// file was hand-edited, truncated, or written by another tool —
// guided recovery instead of a raw failure:
log.Printf("remove %s and restart to mint a new key; session markers signed with the old key will no longer correlate", filepath.Join(home, "runtime", "session.key"))
}
return err
} Prevention
- Never hand-edit or 'set' session.key — the key is machine-generated binary, exactly 32 bytes
- Exclude <home>/runtime from git/cloud-drive sync of the home directory
- After restoring homes from backup, verify wc -c session.key == 32 before starting
- Do not share one home between different product versions that may use different key formats
When it happens
Trigger: A user wrote text into session.key (echo mysecret > session.key); a backup/restore or rsync truncated the file; another tool version wrote a different key size; touch session.key created a 0-byte placeholder.
Common situations: Operators 'setting a key' by hand; partial home-directory syncs via git or cloud drives; mixed-version installs sharing one home.
Related errors
- aes cipher
- budget is below CCR storage minimum
- does not point to an executable
- inspect sqlite parent ACL
- inspect sqlite parent
AI-assisted analysis of JuliusBrussee/caveman@766dce6b13 (2026-08-18).
Data as JSON: /api/errors/395a48085da8370d.
Report an issue: GitHub.
Appendix: source
Thrown at proxy/internal/nativeruntime/marker.go:61
if syncErr := file.Sync(); syncErr != nil {
_ = file.Close()
_ = os.Remove(path)
return nil, fmt.Errorf("native session key sync: %w", syncErr)
}
if closeErr := file.Close(); closeErr != nil {
return nil, fmt.Errorf("native session key close: %w", closeErr)
}
return key, nil
}
if !errors.Is(err, os.ErrExist) {
return nil, fmt.Errorf("native session key create: %w", err)
}
key, err = os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("native session key read: %w", err)
}
if len(key) != sessionKeyBytes {
return nil, fmt.Errorf("native session key length = %d, want %d", len(key), sessionKeyBytes)
}
if err := os.Chmod(path, 0o600); err != nil {
return nil, fmt.Errorf("native session key chmod: %w", err)
}
return key, nil
}
// SessionMarker builds model-temporary correlation context. Local proxy removes
// valid markers byte-surgically before provider inspection or forwarding.
func SessionMarker(key []byte, sessionID string) (string, error) {
if len(key) != sessionKeyBytes || sessionID == "" || len(sessionID) > 256 {
return "", errors.New("native session marker: invalid key or session id")
}
encoded := base64.RawURLEncoding.EncodeToString([]byte(sessionID))
sig := markerMAC(key, encoded)
return fmt.Sprintf(`[[caveman-session-v1 sid="%s" sig="%s"]]`, encoded, sig), nil
}
View on GitHub (pinned to 766dce6b13)