{"record":{"id":"377c5b042d30cb5d","repo":"ipfs/kubo","slug":"expected-mfs-file-didn-t-get-it-this-is-likely","errorCode":null,"errorMessage":"expected *mfs.File, didn't get it. This is likely a race condition","messagePattern":"expected \\*mfs\\.File, didn't get it\\. This is likely a race condition","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/commands/files.go","lineNumber":1571,"sourceCode":"\n\t\tnd := dag.NodeWithData(ft.FilePBData(nil, 0))\n\t\terr = nd.SetCidBuilder(builder)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\terr = pdir.AddChild(fname, nd)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfsn, err := pdir.Child(fname)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfi, ok := fsn.(*mfs.File)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"expected *mfs.File, didn't get it. This is likely a race condition\")\n\t\t}\n\t\treturn fi, nil\n\n\tdefault:\n\t\treturn nil, err\n\t}\n}\n\nfunc checkPath(p string) (string, error) {\n\tif len(p) == 0 {\n\t\treturn \"\", fmt.Errorf(\"paths must not be empty\")\n\t}\n\n\tif p[0] != '/' {\n\t\treturn \"\", fmt.Errorf(\"paths must start with a leading slash\")\n\t}\n\n\tcleaned := gopath.Clean(p)","sourceCodeStart":1553,"sourceCodeEnd":1589,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/core/commands/files.go#L1553-L1589","documentation":"getFileHandle in core/commands/files.go performs an unchecked type assertion of an mfs.FSNode to *mfs.File. If the node at the requested path is not a plain file (e.g. a directory or other FSNode type), or if the MFS tree was concurrently modified between lookup and assertion, the assertion fails and this error is returned instead of panicking.","triggerScenarios":"Calling a files API command (e.g. `ipfs files read`, `files write`) whose handler resolves a path via getFileHandle when (1) the path actually points to a directory rather than a file, or (2) another concurrent mutation (rm/mv/chmod/write) replaces the node between the MFS lookup and the type assertion.","commonSituations":"Scripts running `ipfs files read` on a path that a concurrent `ipfs files rm`/`files mv` just replaced; pointing a write command at a directory path by mistake; parallel tooling mutating MFS while another command holds the path.","solutions":["Verify the path targets a file, not a directory: run `ipfs files stat <path>` and check the type before calling file-only commands.","Re-run the command; if it succeeds the failure was a transient race — serialize concurrent MFS mutations.","Use MFS operations that accept directories (e.g. `files ls`) if the target is a directory."],"exampleFix":"// before: blind call\nipfs files read /some/path\n// after: stat first\nif [ \"$(ipfs files stat --format=\"<type>\" /some/path)\" = \"file\" ]; then\n  ipfs files read /some/path\nfi","handlingStrategy":"type-guard","validationCode":"type=$(ipfs files stat --format='<type>' \"$P\") ; [ \"$type\" = \"file\" ] || echo \"not a file\"","typeGuard":"if f, ok := fsn.(*mfs.File); ok { return f, nil }\nreturn nil, fmt.Errorf(\"path %s is not a regular file\", name)","tryCatchPattern":"fi, err := getFileHandle(node, name)\nif err != nil {\n    if strings.Contains(err.Error(), \"expected *mfs.File\") { /* retry after re-stat or report wrong node type */ }\n    return err\n}","preventionTips":["`ipfs files stat` the path to confirm type=file before file-only commands","Serialize MFS mutations; avoid concurrent rm/mv/write on the same subtree","Create parents with `files mkdir -p` so node types are deterministic"],"tags":["mfs","race-condition","type-assertion","go"],"backgroundTag":"mfs-node-type-race","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"}