JuliusBrussee/caveman · error

native session key chmod dir: %w

Error message

native session key chmod dir: %w

What it means

After MkdirAll succeeds, LoadOrCreateSessionKey explicitly Chmods the runtime directory to 0700 so the session key directory is user-only even if it pre-existed with looser modes. Failure means chmod(2) returned an error — most commonly the process no longer owns the directory (running under a different user than the one that created it) or the filesystem does not support permission changes.

Source

Thrown at proxy/internal/nativeruntime/marker.go:29

	"fmt"
	"os"
	"path/filepath"
	"regexp"
)

const sessionKeyBytes = 32

var markerPattern = regexp.MustCompile(`\[\[caveman-session-v1 sid="([A-Za-z0-9_-]{1,384})" sig="([0-9a-f]{64})"\]\]`)

// LoadOrCreateSessionKey returns one user-only HMAC key shared by CLI adapters
// and local proxy. O_EXCL makes concurrent first startup converge on one key.
func LoadOrCreateSessionKey(home string) ([]byte, error) {
	dir := filepath.Join(home, "runtime")
	if err := os.MkdirAll(dir, 0o700); err != nil {
		return nil, fmt.Errorf("native session key mkdir: %w", err)
	}
	if err := os.Chmod(dir, 0o700); err != nil {
		return nil, fmt.Errorf("native session key chmod dir: %w", err)
	}
	path := filepath.Join(dir, "session.key")
	key := make([]byte, sessionKeyBytes)
	if _, err := rand.Read(key); err != nil {
		return nil, fmt.Errorf("native session key random: %w", err)
	}
	file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
	if err == nil {
		if _, writeErr := file.Write(key); writeErr != nil {
			_ = file.Close()
			_ = os.Remove(path)
			return nil, fmt.Errorf("native session key write: %w", writeErr)
		}
		if syncErr := file.Sync(); syncErr != nil {
			_ = file.Close()
			_ = os.Remove(path)
			return nil, fmt.Errorf("native session key sync: %w", syncErr)
		}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Make ownership consistent: sudo chown -R $(whoami) <home>/runtime
  2. Run the proxy under the same user that owns the home directory
  3. Move the runtime dir off filesystems that ignore chmod, or accept the failure is environmental and remove the pre-existing dir so it is recreated by the running user

Example fix

# before
runtime dir owned by root, proxy runs as 'beagle' -> Error[1067]

# after
sudo chown -R beagle:beagle ~/.caveman/runtime && chmod 700 ~/.caveman/runtime
Defensive patterns

Strategy: validation

Validate before calling

func runtimeDirOwned(home string) bool {
    fi, err := os.Stat(filepath.Join(home, "runtime"))
    return err == nil && fi.IsDir() && fi.Mode().Perm() == 0o700 &&
        os.Geteuid() == /* owner uid of fi */ int(fi.Sys().(*syscall.Stat_t).Uid)
}

Prevention

When it happens

Trigger: Directory created by root during install, then proxy run as normal user; FAT/NTFS or some network filesystems where chmod is unsupported; directory on NFS with root-squash mapping.

Common situations: Mixed-user setups (installed with sudo, run as user, or vice versa); container volumes mounted from Windows hosts.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/a6b950d092a61638. Report an issue: GitHub.