Replacing a pool member missing at import fails: daemon passes vdev GUID as a by-id path

Setup

IncusOS 202609120242
bare metal / 4x SATA HDD zfs-raidz2

What happend

I wanted to replace a drive that shows signs of failing with an new one.

What i did: poweroff, pulled the degrading member, fitted a new blank drive, powered on. storage show then reports the pool DEGRADED with the survivors in devices, the new drive with member_pool: "", and

devices_degraded:
- /dev/disk/by-id/138636987219773271

the vdev GUID, not the missing drive’s by-id path. zpool status (via a container with a delegated dataset):

138636987219773271  UNAVAIL  0 0 0  was /dev/disk/by-id/ata-WDC_WD40EFRX-68WT0N0_WD-WCC6E1PVATZ9-part1

Cause

ZFS names a leaf by GUID when it was absent at import (ZPOOL_CONFIG_NOT_PRESENT); a power-off swap always produces that. getZpoolMembersHelper (internal/storage/storage.go:504-506) prefixes the vdev name with /dev/disk/by-id/, so updateZpoolHelper, which otherwise handles the missing member correctly (zfs.go:782), ends up running

zpool replace <pool> /dev/disk/by-id/138636987219773271 /dev/disk/by-id/<new>

(zfs.go:834). libzfs treats a leading / as a path, finds no such path, and fails with no such device in pool. Nothing is applied; the documented flow cannot succeed. The upstream pull request #1025 test doesn’t hit this because it hot-detaches the disk from a running VM, so the path name survives; any reboot without the disk breaks it.

With a dead drive (mine luckily is only degraded) this would be terminal: the pool stays DEGRADED, the API cannot repair it, and there is no shell to run zpool replace <pool> <guid> <new> by hand.

Suggested fix

For non-ONLINE leaves use the path field from zpool status -j (present even when the name is a GUID; strip the partition suffix), or pass the bare GUID to zpool replace, libzfs should accept it.

Temporary Solution?

Is there a way I can offline replace a disk at the moment?