Vyper doesn't support Solidity's typed custom errors; string messages attached to `assert`/`raise` are the whole mechanism. Keep them short — every character is calldata/bytecode you pay for — but specific enough to debug quickly.
@external
def transfer(to: address, amount: uint256):
assert to != empty(address), "zero address"
assert self.balances[msg.sender] >= amount, "insufficient balance"
self.balances[msg.sender] -= amount
self.balances[to] += amount`empty(address)` produces the zero address in a readable way, and is the idiomatic Vyper equivalent of Solidity's `address(0)`.