diff --git a/src/dstack/_internal/core/backends/aws/compute.py b/src/dstack/_internal/core/backends/aws/compute.py index 5b8df37c38..689dfdbdea 100644 --- a/src/dstack/_internal/core/backends/aws/compute.py +++ b/src/dstack/_internal/core/backends/aws/compute.py @@ -561,9 +561,9 @@ def attach_volume(self, volume: Volume, instance_id: str) -> VolumeAttachmentDat if e.response["Error"]["Code"] == "VolumeInUse": raise ComputeError(f"Failed to attach volume in use: {volume.volume_id}") if e.response["Error"]["Code"] == "InvalidVolume.ZoneMismatch": - raise ComputeError( - f"Failed to attach volume {volume.volume_id}. Volume zone is different from instance zone." - ) + raise ComputeError("Volume zone is different from instance zone") + if e.response["Error"]["Code"] == "InvalidVolume.NotFound": + raise ComputeError("Volume not found") if ( e.response["Error"]["Code"] == "InvalidParameterValue" and f"Invalid value '{device_name}' for unixDevice" diff --git a/src/dstack/_internal/core/backends/base/compute.py b/src/dstack/_internal/core/backends/base/compute.py index f1e2086d94..846527e060 100644 --- a/src/dstack/_internal/core/backends/base/compute.py +++ b/src/dstack/_internal/core/backends/base/compute.py @@ -158,6 +158,7 @@ def delete_volume(self, volume: Volume): def attach_volume(self, volume: Volume, instance_id: str) -> VolumeAttachmentData: """ Attaches a volume to the instance. + If the volume is not found, it should raise `ComputeError()` instead of a thrid-party exception. """ raise NotImplementedError() diff --git a/src/dstack/_internal/core/backends/gcp/compute.py b/src/dstack/_internal/core/backends/gcp/compute.py index 28a7550196..87d044ddc0 100644 --- a/src/dstack/_internal/core/backends/gcp/compute.py +++ b/src/dstack/_internal/core/backends/gcp/compute.py @@ -567,28 +567,34 @@ def delete_volume(self, volume: Volume): def attach_volume(self, volume: Volume, instance_id: str) -> VolumeAttachmentData: zone = get_or_error(volume.provisioning_data).availability_zone - disk = self.disk_client.get( - project=self.config.project_id, - zone=zone, - disk=volume.volume_id, - ) - disk_url = disk.self_link + try: + disk = self.disk_client.get( + project=self.config.project_id, + zone=zone, + disk=volume.volume_id, + ) - attached_disk = compute_v1.AttachedDisk() - attached_disk.source = disk_url - attached_disk.auto_delete = False - attached_disk.device_name = f"pd-{volume.volume_id}" + disk_url = disk.self_link - logger.debug( - "Attaching persistent disk for volume %s to instance %s", volume.volume_id, instance_id - ) - operation = self.instances_client.attach_disk( - project=self.config.project_id, - zone=zone, - instance=instance_id, - attached_disk_resource=attached_disk, - ) - gcp_resources.wait_for_extended_operation(operation, "persistent disk attachment") + attached_disk = compute_v1.AttachedDisk() + attached_disk.source = disk_url + attached_disk.auto_delete = False + attached_disk.device_name = f"pd-{volume.volume_id}" + + logger.debug( + "Attaching persistent disk for volume %s to instance %s", + volume.volume_id, + instance_id, + ) + operation = self.instances_client.attach_disk( + project=self.config.project_id, + zone=zone, + instance=instance_id, + attached_disk_resource=attached_disk, + ) + gcp_resources.wait_for_extended_operation(operation, "persistent disk attachment") + except google.api_core.exceptions.NotFound: + raise ComputeError("Persistent disk not found") logger.debug( "Attached persistent disk for volume %s to instance %s", volume.volume_id, instance_id )