ipfs/kubo · warning
refusing to export key to %s: it changed type while being op
Error message
refusing to export key to %s: it changed type while being opened
What it means
When the output path was seen (at stat time) as a character device or named pipe, writeExportedKeyInPlace opens it without O_TRUNC and re-checks the mode on the open descriptor. If the file descriptor is no longer a char device or pipe, the path's type changed between the stat and the open (TOCTOU race) and the export is refused so a swapped-in regular file is never clobbered. This is an anti-tamper safety check, not an operational failure of the key itself.
Source
Thrown at core/commands/keystore.go:432
// writeExportedKeyInPlace writes to an existing character device or pipe. The
// target type is confirmed on the open descriptor, so a path swapped for a
// regular file after the stat cannot receive the key.
func writeExportedKeyInPlace(outPath string, writeKey func(io.Writer) error) (err error) {
// No O_TRUNC: devices and pipes ignore it, and a regular file that reached
// this path through a race must not be emptied.
file, err := os.OpenFile(outPath, os.O_WRONLY, 0)
if err != nil {
return err
}
defer func() { err = errors.Join(err, file.Close()) }()
info, err := file.Stat()
if err != nil {
return err
}
if info.Mode()&inPlaceModes == 0 {
return fmt.Errorf("refusing to export key to %s: it changed type while being opened", outPath)
}
return writeKey(file)
}
var keyImportCmd = &cmds.Command{
Helptext: cmds.HelpText{
Tagline: "Import a key and prints imported key id",
ShortDescription: `
Imports a key and stores it under the provided name.
By default, the key is assumed to be in 'libp2p-protobuf-cleartext' format,
however it is possible to import private keys wrapped in interoperable PEM PKCS8
by passing '--format=pem-pkcs8-cleartext'.
The PEM format allows for key generation outside of the IPFS node:
$ openssl genpkey -algorithm ED25519 > ed25519.pem
$ ipfs key import test-openssl -f pem-pkcs8-cleartext ed25519.pemView on GitHub (pinned to 329838acdf)
Solutions
- Re-run the export; the race is transient and a second run usually sees a stable type
- Make sure nothing else concurrently recreates the fifo/device at that path while exporting
- If targeting a moving path, export to a stable regular-file path instead
- Treat repeated occurrences as suspicious environment interference and investigate the other process
Example fix
// before ipfs key export mykey -o /tmp/pipe-that-a-rotation-script-recreates // after ipfs key export mykey -o /tmp/mykey.pem # stable regular-file path
Defensive patterns
Strategy: retry
Validate before calling
# ensure no concurrent job recreates the target while exporting flock /tmp/export.lock -c 'ipfs key export mykey -o /tmp/myfifo'
Try / catch
if err := writeExportedKey(outPath, r, fmt); err != nil && strings.Contains(err.Error(), "changed type while being opened") {
time.Sleep(100 * time.Millisecond)
return writeExportedKey(outPath, r, fmt) // one retry after the racing writer settles
} Prevention
- Serialize exports and path-rotating scripts with a lock file
- Export to stable regular-file paths instead of rotating fifos
- Investigate repeated occurrences — another process is swapping the path
When it happens
Trigger: Another process replaces the output path (e.g. /dev/stdout, a fifo) with a regular file — via rename/unlink+create — in the window between the initial stat and the open during `ipfs key export -o <path>`.
Common situations: Concurrent scripts rotating or recreating fifos/device symlinks while an export runs; a path like /dev/stdout re-evaluated in a subshell whose fd layout changed; adversarial/racing environments.
Related errors
- refusing to export key to %s: not a regular file, character
- creating temporary file for %s: %w
- flushing %s: %w
- writing %s: %w
- too many levels of symbolic links: %s
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/7e3ca326610b98b6.
Report an issue: GitHub.