ipfs/kubo · error
%q is not a file
Error message
%q is not a file
What it means
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.
Source
Thrown at repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go:125
if f.openErr != nil {
return nil, f.openErr
}
iPath, err := parsePath(gopath.Join(f.distPath, filePath))
if err != nil {
return nil, err
}
nd, err := f.ipfs.Unixfs().Get(ctx, iPath)
if err != nil {
return nil, err
}
f.recordFetched(iPath)
fileNode, ok := nd.(files.File)
if !ok {
return nil, fmt.Errorf("%q is not a file", filePath)
}
var rc io.ReadCloser
if f.limit != 0 {
rc = migrations.NewLimitReadCloser(fileNode, f.limit)
} else {
rc = fileNode
}
defer rc.Close()
return io.ReadAll(rc)
}
func (f *IpfsFetcher) Close() error {
f.closeOnce.Do(func() {
if f.ipfsStopFunc != nil {
// Tell ipfs node to stop and wait for it to stop
f.ipfsStopFunc()View on GitHub (pinned to 329838acdf)
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
Example fix
// before
nd, _ := f.node.Get(ctx, iPath)
fileNode, ok := nd.(files.File)
if !ok { return nil, fmt.Errorf("%q is not a file", filePath) }
// after
nd, _ := f.node.Get(ctx, iPath)
if dir, isDir := nd.(files.Directory); isDir {
return nil, fmt.Errorf("%q resolved to a directory; expected a file", filePath)
}
fileNode, ok := nd.(files.File)
if !ok { return nil, fmt.Errorf("%q is not a file", filePath) } Defensive patterns
Strategy: type-guard
Validate before calling
// check the path is a file before fetching
stat, _ := f.node.Unixfs().Stat(ctx, iPath)
if stat.Type != iofs.ModeRegular { return fmt.Errorf("expected a file, got %v", stat.Type) } Type guard
fileNode, ok := nd.(files.File)
if !ok {
if _, isDir := nd.(files.Directory); isDir { return nil, errors.New("resolved path is a directory") }
return nil, fmt.Errorf("unsupported node type for %q", filePath)
} Try / catch
nd, err := fetcher.Fetch(ctx, path)
if err != nil && strings.HasSuffix(err.Error(), "is not a file") {
// verify the artifact path/CID points to a regular file
} Prevention
- Verify migration artifact paths resolve to files, not directories
- Use canonical migration manifest paths/CIDs
- Add a type check for files.Directory before casting
When it happens
Trigger: 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.
Common situations: Misconfigured IPFS_FETCHER path pointing at a directory; network corruption or a wrong migration manifest entry; testing the fetcher with a directory CID.
Related errors
- unsupported file type '%s'
- file type %d not supported
- nocopy option requires '--raw-leaves' to be enabled as well
- CIDv0 only supports sha2-256
- assets: could load Asset '%s': %s
AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03).
Data as JSON: /api/errors/0867816eb8a0be5d.
Report an issue: GitHub.