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.pem

View on GitHub (pinned to 329838acdf)

Solutions

  1. Re-run the export; the race is transient and a second run usually sees a stable type
  2. Make sure nothing else concurrently recreates the fifo/device at that path while exporting
  3. If targeting a moving path, export to a stable regular-file path instead
  4. 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

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


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