Unified error type. Backend errors are erased to a small enum + opaque code so the hot path never allocates.