{"record":{"id":"0867816eb8a0be5d","repo":"ipfs/kubo","slug":"q-is-not-a-file","errorCode":null,"errorMessage":"%q is not a file","messagePattern":"%q is not a file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go","lineNumber":125,"sourceCode":"\tif f.openErr != nil {\n\t\treturn nil, f.openErr\n\t}\n\n\tiPath, err := parsePath(gopath.Join(f.distPath, filePath))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnd, err := f.ipfs.Unixfs().Get(ctx, iPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tf.recordFetched(iPath)\n\n\tfileNode, ok := nd.(files.File)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"%q is not a file\", filePath)\n\t}\n\n\tvar rc io.ReadCloser\n\tif f.limit != 0 {\n\t\trc = migrations.NewLimitReadCloser(fileNode, f.limit)\n\t} else {\n\t\trc = fileNode\n\t}\n\tdefer rc.Close()\n\n\treturn io.ReadAll(rc)\n}\n\nfunc (f *IpfsFetcher) Close() error {\n\tf.closeOnce.Do(func() {\n\t\tif f.ipfsStopFunc != nil {\n\t\t\t// Tell ipfs node to stop and wait for it to stop\n\t\t\tf.ipfsStopFunc()","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/ipfs/kubo/blob/329838acdfafae224582930457efe80aa217afc0/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go#L107-L143","documentation":"IpfsFetcher.Fetch successfully resolved the given path over IPFS but the returned node is not a files.File (e.g. it is a directory or symlink-only DAG), so it cannot be read as a stream. The fetcher downloads migration artifacts, which must be regular files.","triggerScenarios":"Fetching a migrations artifact path that actually resolves to a directory (wrong CAR path or version), or a CID whose root is a UnixFS directory; typo'd fetch target in a custom fetcher config.","commonSituations":"Misconfigured IPFS_FETCHER path pointing at a directory; network corruption or a wrong migration manifest entry; testing the fetcher with a directory CID.","solutions":["Verify the fetch path/CID points to a regular UnixFS file (check with `ipfs ls` or `ipfs stat`)","Fix the migrations artifact URL/path in the fetcher configuration to the correct file","If you control the fetcher code, handle files.Directory by descending or erroring with a clearer message"],"exampleFix":"// before\nnd, _ := f.node.Get(ctx, iPath)\nfileNode, ok := nd.(files.File)\nif !ok { return nil, fmt.Errorf(\"%q is not a file\", filePath) }\n// after\nnd, _ := f.node.Get(ctx, iPath)\nif dir, isDir := nd.(files.Directory); isDir {\n    return nil, fmt.Errorf(\"%q resolved to a directory; expected a file\", filePath)\n}\nfileNode, ok := nd.(files.File)\nif !ok { return nil, fmt.Errorf(\"%q is not a file\", filePath) }","handlingStrategy":"type-guard","validationCode":"// check the path is a file before fetching\nstat, _ := f.node.Unixfs().Stat(ctx, iPath)\nif stat.Type != iofs.ModeRegular { return fmt.Errorf(\"expected a file, got %v\", stat.Type) }","typeGuard":"fileNode, ok := nd.(files.File)\nif !ok {\n    if _, isDir := nd.(files.Directory); isDir { return nil, errors.New(\"resolved path is a directory\") }\n    return nil, fmt.Errorf(\"unsupported node type for %q\", filePath)\n}","tryCatchPattern":"nd, err := fetcher.Fetch(ctx, path)\nif err != nil && strings.HasSuffix(err.Error(), \"is not a file\") {\n    // verify the artifact path/CID points to a regular file\n}","preventionTips":["Verify migration artifact paths resolve to files, not directories","Use canonical migration manifest paths/CIDs","Add a type check for files.Directory before casting"],"tags":["go","migrations","ipfs","unixfs"],"backgroundTag":"unexpected-unixfs-node-type","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"}