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

  1. Verify the fetch path/CID points to a regular UnixFS file (check with `ipfs ls` or `ipfs stat`)
  2. Fix the migrations artifact URL/path in the fetcher configuration to the correct file
  3. 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

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


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