{"record":{"id":"7e3ca326610b98b6","repo":"ipfs/kubo","slug":"refusing-to-export-key-to-s-it-changed-type-whil","errorCode":null,"errorMessage":"refusing to export key to %s: it changed type while being opened","messagePattern":"refusing to export key to (.+?): it changed type while being opened","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/commands/keystore.go","lineNumber":432,"sourceCode":"\n// writeExportedKeyInPlace writes to an existing character device or pipe. The\n// target type is confirmed on the open descriptor, so a path swapped for a\n// regular file after the stat cannot receive the key.\nfunc writeExportedKeyInPlace(outPath string, writeKey func(io.Writer) error) (err error) {\n\t// No O_TRUNC: devices and pipes ignore it, and a regular file that reached\n\t// this path through a race must not be emptied.\n\tfile, err := os.OpenFile(outPath, os.O_WRONLY, 0)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer func() { err = errors.Join(err, file.Close()) }()\n\n\tinfo, err := file.Stat()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif info.Mode()&inPlaceModes == 0 {\n\t\treturn fmt.Errorf(\"refusing to export key to %s: it changed type while being opened\", outPath)\n\t}\n\treturn writeKey(file)\n}\n\nvar keyImportCmd = &cmds.Command{\n\tHelptext: cmds.HelpText{\n\t\tTagline: \"Import a key and prints imported key id\",\n\t\tShortDescription: `\nImports a key and stores it under the provided name.\n\nBy default, the key is assumed to be in 'libp2p-protobuf-cleartext' format,\nhowever it is possible to import private keys wrapped in interoperable PEM PKCS8\nby passing '--format=pem-pkcs8-cleartext'.\n\nThe PEM format allows for key generation outside of the IPFS node:\n\n  $ openssl genpkey -algorithm ED25519 > ed25519.pem\n  $ ipfs key import test-openssl -f pem-pkcs8-cleartext ed25519.pem","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/commands/keystore.go#L414-L450","documentation":"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.","triggerScenarios":"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>`.","commonSituations":"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.","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"],"exampleFix":"// before\nipfs key export mykey -o /tmp/pipe-that-a-rotation-script-recreates\n// after\nipfs key export mykey -o /tmp/mykey.pem   # stable regular-file path","handlingStrategy":"retry","validationCode":"# ensure no concurrent job recreates the target while exporting\nflock /tmp/export.lock -c 'ipfs key export mykey -o /tmp/myfifo'","typeGuard":null,"tryCatchPattern":"if err := writeExportedKey(outPath, r, fmt); err != nil && strings.Contains(err.Error(), \"changed type while being opened\") {\n    time.Sleep(100 * time.Millisecond)\n    return writeExportedKey(outPath, r, fmt) // one retry after the racing writer settles\n}","preventionTips":["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"],"tags":["cli","keystore","filesystem","race-condition","security"],"backgroundTag":"toctou-file-type-change","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"}