{"record":{"id":"eb3bba3289d060f1","repo":"sipeed/picoclaw","slug":"credential-keygen-write-private-key-q-w","errorCode":null,"errorMessage":"credential: keygen: write private key %q: %w","messagePattern":"credential: keygen: write private key %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/credential/keygen.go","lineNumber":46,"sourceCode":"func GenerateSSHKey(path string) error {\n\tif err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: cannot create directory %q: %w\", filepath.Dir(path), err)\n\t}\n\n\tpubRaw, privRaw, err := ed25519.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: ed25519 key generation failed: %w\", err)\n\t}\n\n\t// Marshal private key as OpenSSH PEM.\n\tblock, err := ssh.MarshalPrivateKey(privRaw, \"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: marshal private key: %w\", err)\n\t}\n\tprivPEM := pem.EncodeToMemory(block)\n\n\tif err = os.WriteFile(path, privPEM, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: write private key %q: %w\", path, err)\n\t}\n\n\t// Marshal public key as authorized_keys line.\n\tsshPub, err := ssh.NewPublicKey(pubRaw)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: marshal public key: %w\", err)\n\t}\n\tpubLine := ssh.MarshalAuthorizedKey(sshPub)\n\n\tpubPath := path + \".pub\"\n\tif err := os.WriteFile(pubPath, pubLine, 0o644); err != nil {\n\t\treturn fmt.Errorf(\"credential: keygen: write public key %q: %w\", pubPath, err)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":28,"sourceCodeEnd":63,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/credential/keygen.go#L28-L63","documentation":"GenerateSSHKey writes the OpenSSH PEM private key with os.WriteFile(path, privPEM, 0600) and wraps failures. Common causes: permission denied on the directory, disk full (ENOSPC/EDQUOT), a directory existing at the key path, or an immutable existing file. Because the private key is written before the .pub file, a failure here leaves nothing behind (the pub write at line 58 never happens).","triggerScenarios":"GenerateSSHKey where the parent dir is not writable by the current user (dir was created 0700 by another uid); /tmp full in ephemeral CI; path is an existing directory; previous key file has the immutable bit set.","commonSituations":"Keygen as root after dir creation as user (or vice versa); small disks / quota-limited home directories; containers with size-limited tmpfs; leftover directory named picoclaw_ed25519.key from a failed run.","solutions":["Check writability and space: `ls -ld <dir>`, `df -h <dir>`, then free space or fix ownership","Ensure no directory occupies the key path: `ls -ld <path>`","If an immutable/locked file exists, remove it first (`chattr -i` on Linux if applicable)","Re-run the keygen after fixing the filesystem issue - the function overwrites existing files cleanly"],"exampleFix":"// before\nif err := credential.GenerateSSHKey(path); err != nil {\n    return err\n}\n\n// after: pre-flight the exact failure modes\nif fi, err := os.Stat(path); err == nil && fi.IsDir() {\n    return fmt.Errorf(\"%s is a directory\", path)\n}\ndir := filepath.Dir(path)\nif err := unix.Access(dir, unix.W_OK); err != nil {\n    return fmt.Errorf(\"no write access to %s: %w\", dir, err)\n}\nif err := credential.GenerateSSHKey(path); err != nil {\n    return err\n}","handlingStrategy":"validation","validationCode":"func keyPathWritable(path string) error {\n    if fi, err := os.Stat(path); err == nil && fi.IsDir() {\n        return fmt.Errorf(\"%s is a directory\", path)\n    }\n    dir := filepath.Dir(path)\n    f, err := os.OpenFile(filepath.Join(dir, \".keygen-probe\"), os.O_CREATE|os.O_WRONLY, 0o600)\n    if err != nil {\n        return fmt.Errorf(\"directory %s not writable: %w\", dir, err)\n    }\n    f.Close()\n    os.Remove(filepath.Join(dir, \".keygen-probe\"))\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-check directory ownership/mode (0700, daemon uid) before keygen","Monitor disk space on the key filesystem; ENOSPC is the top cause","Provision the parent directory in config management instead of improvising at runtime"],"tags":["filesystem","permissions","disk-full","keygen"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}