Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion crates/openshell-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2045,7 +2045,16 @@ pub async fn sandbox_create(
name: name.unwrap_or_default().to_string(),
};

let response = client.create_sandbox(request).await.into_diagnostic()?;
let response = match client.create_sandbox(request).await {
Ok(resp) => resp,
Err(status) if status.code() == Code::AlreadyExists => {
return Err(miette::miette!(
"{}\n\nhint: delete it first with: openshell sandbox delete <name>\n or use a different name",
status.message()
));
}
Err(status) => return Err(status).into_diagnostic(),
};
let sandbox = response
.into_inner()
.sandbox
Expand Down
14 changes: 14 additions & 0 deletions crates/openshell-server/src/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,20 @@ impl OpenShell for OpenShellService {
..Default::default()
};

// Reject duplicate names early, before touching the index or store.
// This mirrors the provider-creation pattern (see `create_provider`).
let existing = self
.state
.store
.get_message_by_name::<Sandbox>(&name)
.await
.map_err(|e| Status::internal(format!("fetch sandbox failed: {e}")))?;
if existing.is_some() {
return Err(Status::already_exists(format!(
"sandbox '{name}' already exists"
)));
}

// Persist to the store FIRST so the sandbox watcher always finds
// the record with `spec` populated. If we created the k8s
// resource first, the watcher could race us and write a fallback
Expand Down
5 changes: 0 additions & 5 deletions e2e/python/test_inference_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,6 @@ def _upsert_managed_inference(
except grpc.RpcError as create_exc:
if create_exc.code() == grpc.StatusCode.ALREADY_EXISTS:
continue
if (
create_exc.code() == grpc.StatusCode.INTERNAL
and "UNIQUE constraint failed" in (create_exc.details() or "")
):
continue
raise
else:
raise RuntimeError("failed to upsert managed e2e provider after retries")
Expand Down
Loading