{"record":{"id":"12ddbaf60c9cb3b5","repo":"ipfs/kubo","slug":"saving-old-key-in-keystore-v","errorCode":null,"errorMessage":"saving old key in keystore (%v)","messagePattern":"saving old key in keystore \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/commands/keystore.go","lineNumber":808,"sourceCode":"\t\t\toptions.Key.Type(algorithm),\n\t\t})\n\t} else {\n\t\tidentity, err = config.CreateIdentity(out, []options.KeyGenerateOption{\n\t\t\toptions.Key.Type(algorithm),\n\t\t})\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"creating identity (%v)\", err)\n\t}\n\n\t// Save old identity to keystore\n\toldPrivKey, err := cfg.Identity.DecodePrivateKey(\"\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decoding old private key (%v)\", err)\n\t}\n\tkeystore := repo.Keystore()\n\tif err := keystore.Put(oldKey, oldPrivKey); err != nil {\n\t\treturn fmt.Errorf(\"saving old key in keystore (%v)\", err)\n\t}\n\n\t// Update identity\n\tcfg.Identity = identity\n\n\t// Write config file to repo\n\tif err = repo.SetConfig(cfg); err != nil {\n\t\treturn fmt.Errorf(\"saving new key to config (%v)\", err)\n\t}\n\treturn nil\n}\n\nfunc keyOutputListEncoders() cmds.EncoderFunc {\n\treturn cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, list *KeyOutputList) error {\n\t\twithID, _ := req.Options[\"l\"].(bool)\n\n\t\ttw := tabwriter.NewWriter(w, 1, 2, 1, ' ', 0)\n\t\tfor _, s := range list.Keys {","sourceCodeStart":790,"sourceCodeEnd":826,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/commands/keystore.go#L790-L826","documentation":"After decoding the old private key, doRotate writes it into the repo keystore under the --old-key name via keystore.Put. Failure here is wrapped as \"saving old key in keystore (%v)\" — typically a keystore write error (bad name, filesystem failure). The rotate aborts before the config is modified, so the node identity is unchanged.","triggerScenarios":"The chosen oldKey name fails keystore naming rules (invalid characters); filesystem permission or disk-full errors writing $IPFS_PATH/keystore/<name>; keystore file locked/unreadable.","commonSituations":"Passing a name with slashes or illegal characters via --old-key; repo directory owned by another user; read-only filesystem; full disk.","solutions":["Use a simple alphanumeric keystore name for --old-key (e.g. old-self-2024)","Check $IPFS_PATH/keystore permissions and free disk space (`df -h`)","Retry after fixing the filesystem issue; rotate is safe to retry since config is only written after this step succeeds","Inspect the wrapped (%v) message for the exact keystore error"],"exampleFix":"// before\nipfs key rotate --old-key=\"my/old key\"\n// after\nipfs key rotate --old-key=old-key-2024","handlingStrategy":"validation","validationCode":"name=\"old-self-2024\"\ncase \"$name\" in *[!A-Za-z0-9_-]*) echo \"invalid keystore name\"; exit 1;; esac\ndf -h \"$IPFS_PATH\" | awk 'NR==2 {exit ($5+0 >= 95) ? 1 : 0}' || { echo 'disk almost full'; exit 1; }","typeGuard":"func safeKeystoreName(name string) bool {\n    if name == \"\" || name == \"self\" { return false }\n    for _, r := range name {\n        if !(r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '_') {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"if err := doRotate(...); err != nil {\n    if strings.Contains(err.Error(), \"saving old key in keystore\") {\n        // safe to retry: config untouched; fix name/disk/permissions first\n    }\n}","preventionTips":["Use simple alphanumeric keystore names","Ensure adequate free disk space before rotate","Run as the repo-owning user"],"tags":["keystore","filesystem","cli"],"backgroundTag":"keystore-write-failed","analyzedSha":"329838acdfafae224582930457efe80aa217afc0","analyzedAt":"2026-09-03T18:30:52.135Z","contentChangedAt":"2026-09-03T18:30:52.135Z","schemaVersion":2},"datasetVersion":"2026-09-11T00:17:11.886Z"}