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

  1. Restore Identity.PrivKey to the base64 string format: `ipfs config show | jq .Identity.PrivKey` should print a quoted string
  2. Re-set it as a string: `ipfs config --json Identity.PrivKey '"<base64-key>"'` (then verify with `ipfs id` after daemon restart)
  3. If the raw key material was destroyed, re-init the repo or restore from backup — the PeerID changes otherwise
  4. 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

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


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/c941c88f661af6a3. Report an issue: GitHub.