sipeed/picoclaw · error

credential: cannot determine home directory: %w

Error message

credential: cannot determine home directory: %w

What it means

DefaultSSHKeyPath calls os.UserHomeDir, which reads $HOME on Unix (%USERPROFILE% on Windows) and errors when it is empty. The picoclaw credential layer then cannot compute ~/.ssh/picoclaw_ed25519.key for key auto-detection or keygen defaulting. The underlying error is wrapped, so it is diagnosable via err.Error() text from the environment API.

Source

Thrown at pkg/credential/keygen.go:19

package credential

import (
	"crypto/ed25519"
	"crypto/rand"
	"encoding/pem"
	"fmt"
	"os"
	"path/filepath"

	"golang.org/x/crypto/ssh"
)

// DefaultSSHKeyPath returns the canonical path for the picoclaw-specific SSH key.
// The path is always ~/.ssh/picoclaw_ed25519.key (os.UserHomeDir is cross-platform).
func DefaultSSHKeyPath() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("credential: cannot determine home directory: %w", err)
	}
	return filepath.Join(home, ".ssh", "picoclaw_ed25519.key"), nil
}

// GenerateSSHKey generates an Ed25519 SSH key pair and writes the private key
// to path (permissions 0600) and the public key to path+".pub" (permissions 0644).
// The ~/.ssh/ directory is created with 0700 if it does not exist.
// If the files already exist they are overwritten.
func GenerateSSHKey(path string) error {
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		return fmt.Errorf("credential: keygen: cannot create directory %q: %w", filepath.Dir(path), err)
	}

	pubRaw, privRaw, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		return fmt.Errorf("credential: keygen: ed25519 key generation failed: %w", err)
	}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Set HOME explicitly in the service environment: systemd `Environment="HOME=/var/lib/picoclaw"`, docker `-e HOME=/root`, or `sudo -E`
  2. Bypass home lookup entirely: export PICOCLAW_SSH_KEY_PATH=<absolute key path>, which pickSSHKeyPath honors before auto-detection
  3. For containers, add `ENV HOME=/root` (or the USER's home) to the Dockerfile

Example fix

# before: systemd unit with no HOME
[Service]
ExecStart=/usr/local/bin/picoclaw

# after: explicit home (or bypass via key path env)
[Service]
Environment="HOME=/var/lib/picoclaw"
# alternative: Environment="PICOCLAW_SSH_KEY_PATH=/etc/picoclaw/picoclaw_ed25519.key"
ExecStart=/usr/local/bin/picoclaw
Defensive patterns

Strategy: validation

Validate before calling

func homeResolved() error {
    if os.Getenv("HOME") == "" { // covers the Unix UserHomeDir failure
        return fmt.Errorf("HOME is not set; set HOME or PICOCLAW_SSH_KEY_PATH")
    }
    return nil
}

Prevention

When it happens

Trigger: Calling DefaultSSHKeyPath (directly or via findDefaultSSHKey / GenerateSSHKey defaults) in a process whose environment lacks HOME: bare systemd services without Environment=, cron jobs, `sudo` without -E, docker run without -e HOME, env -i wrappers, or some CI runners.

Common situations: Deploying the daemon as a systemd unit and forgetting Environment="HOME=/var/lib/picoclaw"; Docker images using USER without setting HOME for that uid; running via crontab where HOME may be unset; macOS launchd agents.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/69086fe9790eea89. Report an issue: GitHub.