ipfs/kubo · error
private key in config was not a string
Error message
private key in config was not a string
What it means
After fetching the PrivKey config field, nodePeerID asserts the value is a Go string. This error fires if Identity.PrivKey exists in the config but is not a string (e.g. stored as a JSON object, number, or array). A base64-encoded key string is required to decode the private key.
Source
Thrown at core/commands/config.go:634
editor = editorAndArgs[0]
args := append(editorAndArgs[1:], filename)
cmd := exec.Command(editor, args...)
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
return cmd.Run()
}
// nodePeerID derives the PeerID implied by the private key stored in the repo
// config. Identity.PeerID must equal this value; the node refuses to start
// when the two disagree.
func nodePeerID(r repo.Repo) (peer.ID, error) {
keyF, err := getConfig(r, config.PrivKeySelector)
if err != nil {
return "", errors.New("failed to get PrivKey")
}
pkstr, ok := keyF.Value.(string)
if !ok {
return "", errors.New("private key in config was not a string")
}
ident := config.Identity{PrivKey: pkstr}
pk, err := ident.DecodePrivateKey("")
if err != nil {
return "", fmt.Errorf("failed to decode PrivKey: %w", err)
}
id, err := peer.IDFromPrivateKey(pk)
if err != nil {
return "", fmt.Errorf("failed to derive PeerID from PrivKey: %w", err)
}
return id, nil
}
func replaceConfig(r repo.Repo, file io.Reader) error {
var newCfg config.Config
if err := json.NewDecoder(file).Decode(&newCfg); err != nil {
return errors.New("failed to decode file as config")
}View on GitHub (pinned to 329838acdf)
Solutions
- Restore Identity.PrivKey to the base64 string format: `ipfs config show | jq .Identity.PrivKey` should print a quoted string
- Re-set it as a string: `ipfs config --json Identity.PrivKey '"<base64-key>"'` (then verify with `ipfs id` after daemon restart)
- If the raw key material was destroyed, re-init the repo or restore from backup — the PeerID changes otherwise
- Avoid hand-editing Identity via scripts; use `ipfs config replace` with a file that preserves the original Identity block
Example fix
// before (config.json)
"Identity": { "PrivKey": { "Type": 0, "Data": "..." } }
// after
"Identity": { "PrivKey": "CAESQA==...base64..." } Defensive patterns
Strategy: type-guard
Validate before calling
key=$(ipfs config show | jq -r '.Identity.PrivKey'); case "$(echo -n "$key" | jq -R 'fromjson? | type' 2>/dev/null)" in null|"\"string\"") ;; *) echo "PrivKey is not a JSON string" ;; esac
Type guard
func isStringPrivKey(v any) (string, bool) {
s, ok := v.(string)
return s, ok // ok==false means Identity.PrivKey is a non-string JSON value
} Try / catch
keyF, err := getConfig(r, config.PrivKeySelector)
if err != nil { return err }
pkstr, ok := keyF.Value.(string)
if !ok {
return fmt.Errorf("Identity.PrivKey must be a base64 string, got %T", keyF.Value)
} Prevention
- Store PrivKey only as the base64 string kubo generates at init
- Never let jq/scripts restructure Identity.PrivKey into nested JSON
- Validate config.json with jq (check types) after scripted edits
- Use `ipfs config --json Identity.PrivKey '"<b64>"'` to set it correctly
When it happens
Trigger: Hand-editing config.json so Identity.PrivKey becomes a non-string JSON value; programmatic config manipulation that sets Identity.PrivKey to an object/array; a corrupted config where the field type changed.
Common situations: Users pasting the key as nested JSON (e.g. {"Type":0,"Data":...}) instead of the expected base64 string produced by `ipfs key export`/init; scripted config edits with jq that accidentally restructure the field.
Related errors
- unrecognized key type: %s
- cannot set Identity.PeerID to a value that does not match th
- failed to get PrivKey
- failed to decode PrivKey: %w
- decoding old private key (%v)
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/c941c88f661af6a3.
Report an issue: GitHub.