containerd/containerd · error

failed to get block device size: %s: %w

Error message

failed to get block device size: %s: %w

What it means

makeThinPoolMapping builds the thin-pool table and must know the data device's byte size, which it obtains via BlockDeviceSize (an lseek to end of file). This error wraps whatever failure occurred while sizing the data file/device, aborting pool creation or reload.

Source

Thrown at plugins/snapshots/devmapper/dmsetup/dmsetup.go:103

	thinPool, err := makeThinPoolMapping(dataFile, metaFile, blockSizeSectors)
	if err != nil {
		return err
	}

	_, err = dmsetup("reload", deviceName, "--table", thinPool)
	return err
}

const (
	lowWaterMark = 32768                // Picked arbitrary, might need tuning
	skipZeroing  = "skip_block_zeroing" // Skipping zeroing to reduce latency for device creation
)

// makeThinPoolMapping makes thin-pool table entry
func makeThinPoolMapping(dataFile, metaFile string, blockSizeSectors uint32) (string, error) {
	dataDeviceSizeBytes, err := BlockDeviceSize(dataFile)
	if err != nil {
		return "", fmt.Errorf("failed to get block device size: %s: %w", dataFile, err)
	}

	// Thin-pool mapping target has the following format:
	// start - starting block in virtual device
	// length - length of this segment
	// metadata_dev - the metadata device
	// data_dev - the data device
	// data_block_size - the data block size in sectors
	// low_water_mark - the low water mark, expressed in blocks of size data_block_size
	// feature_args - the number of feature arguments
	// args
	lengthSectors := dataDeviceSizeBytes / SectorSize
	target := fmt.Sprintf("0 %d thin-pool %s %s %d %d 1 %s",
		lengthSectors,
		metaFile,
		dataFile,
		blockSizeSectors,
		lowWaterMark,

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Check the wrapped cause (%w) in the error — it names the underlying open/seek failure.
  2. Verify the data file/device path exists and is readable/writable by the process.
  3. Recreate the pool: ensure the sparse data file is created with correct size before makeThinPoolMapping runs.
  4. Check host disk health/space and that the loopback device is attached (losetup).

Example fix

// before
err := pool.CreatePool() // data file never created
// after
if err := createSparseFile(dataFile, sizeBytes); err != nil { return err }
if err := pool.CreatePool(); err != nil { return fmt.Errorf("pool create: %w", err) }
Defensive patterns

Strategy: try-catch

Validate before calling

func checkDataDevice(path string) error {
    fi, err := os.Stat(path)
    if err != nil { return fmt.Errorf("data device missing: %w", err) }
    if !fi.Mode().IsRegular() && fi.Mode()&os.ModeDevice == 0 {
        return fmt.Errorf("%s is not a seekable file/device", path)
    }
    return nil
}

Try / catch

table, err := CreatePool(...)
if err != nil {
    var sizeErr error
    if strings.Contains(err.Error(), "failed to get block device size") {
        sizeErr = checkDataDevice(dataFile)
    }
    return fmt.Errorf("pool create failed: %w (device check: %v)", err, sizeErr)
}

Prevention

When it happens

Trigger: CreatePool or ReloadPool -> makeThinPoolMapping -> BlockDeviceSize fails: the data file/device path is wrong, cannot be opened, or the seek to SEEK_END fails.

Common situations: The loopback data file was not created before pool setup; permission problems on /dev or the data dir; the underlying block device disappeared (VM disk detach); filesystem errors on the host.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/689e520d2cd4498f. Report an issue: GitHub.