sipeed/picoclaw · error
credential: keygen: write public key %q: %w
Error message
credential: keygen: write public key %q: %w
What it means
The final step of GenerateSSHKey writes the authorized_keys-format public key to path+".pub" with mode 0644, wrapping os.WriteFile failures. The private key at `path` was already written successfully, so this error leaves a partial key pair: a private key with no matching .pub file. The message includes the exact pub path.
Source
Thrown at pkg/credential/keygen.go:58
if err != nil {
return fmt.Errorf("credential: keygen: marshal private key: %w", err)
}
privPEM := pem.EncodeToMemory(block)
if err = os.WriteFile(path, privPEM, 0o600); err != nil {
return fmt.Errorf("credential: keygen: write private key %q: %w", path, err)
}
// Marshal public key as authorized_keys line.
sshPub, err := ssh.NewPublicKey(pubRaw)
if err != nil {
return fmt.Errorf("credential: keygen: marshal public key: %w", err)
}
pubLine := ssh.MarshalAuthorizedKey(sshPub)
pubPath := path + ".pub"
if err := os.WriteFile(pubPath, pubLine, 0o644); err != nil {
return fmt.Errorf("credential: keygen: write public key %q: %w", pubPath, err)
}
return nil
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Free space or raise quota on the target filesystem (`df -h <dir>`), then re-run keygen
- Clean up the partial state before retrying: remove both `path` and `path+".pub"` so the pair is regenerated atomically fresh
- Fix permissions on the directory if the .pub specifically cannot be created
- After any successful re-run, verify the pair matches: compare `ssh-keygen -y -f <path>` output to the .pub contents
Example fix
// before
if err := credential.GenerateSSHKey(path); err != nil {
return err // possible orphaned private key with no .pub
}
// after: treat keygen as a transaction - clean partial state on failure
if err := credential.GenerateSSHKey(path); err != nil {
os.Remove(path)
os.Remove(path + ".pub")
return fmt.Errorf("keygen failed (partial files removed): %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
func spaceForKeypair(dir string) error {
var st syscall.Statfs_t
if err := syscall.Statfs(dir, &st); err != nil {
return err
}
if st.Bavail*uint64(st.Bsize) < 1<<20 { // need ~1 MiB headroom
return fmt.Errorf("less than 1 MiB free in %s", dir)
}
return nil
} Try / catch
if err := credential.GenerateSSHKey(path); err != nil {
if strings.Contains(err.Error(), "write public key") {
// private key was already written; remove partial pair before any retry
os.Remove(path)
os.Remove(path + ".pub")
return fmt.Errorf("keygen incomplete, partial files cleaned: %w", err)
}
return err
} Prevention
- Treat keygen as a transaction: on any failure, delete both path and path+".pub" before retrying
- Ensure ~1 MiB free space and directory writability before generating keys
- After keygen, verify the pair matches (ssh-keygen -y -f priv vs .pub) in your provisioning checks
When it happens
Trigger: Disk fills up between the private and public key writes; directory perms change mid-run; quota hit (EDQUOT); existing .pub is immutable or a directory. ENOSPC after a large private-key write is the classic case.
Common situations: Tiny home partitions or container overlay limits; quota systems kicking in at the second write; antivirus/immunetable file watchers locking the .pub on some platforms; interrupted keygen retried into a half-state.
Related errors
- credential: keygen: cannot create directory %q: %w
- credential: keygen: write private key %q: %w
- write result: %w
- close destination file %s: %w
- write temp file: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/8e924aa008a996ee.
Report an issue: GitHub.