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

  1. 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
  2. If you have the correct key material, restore exactly 32 bytes (re-copy the original file, do not retype it)
  3. Verify: wc -c <home>/runtime/session.key must print 32
  4. 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

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


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)