xpipe-io/xpipe · error · BeaconClientException
Cannot delete category: " + cat.getName()
Error message
Cannot delete category: " + cat.getName()
What it means
XPipe's beacon API refuses to delete a data store category when DataStorage.canDeleteStoreCategory(cat) returns false. Certain categories are protected (e.g. built-in/default categories, or categories that still contain stores or sub-categories), so the daemon rejects removal instead of corrupting its storage hierarchy. The exception is a BeaconClientException, meaning it is reported back to the API client as a request-level failure.
Source
Thrown at app/src/main/java/io/xpipe/app/beacon/api/CategoryRemoveExchange.java:34
import java.util.UUID;
public class CategoryRemoveExchange extends BeaconInterface<CategoryRemoveExchange.Request> {
@Override
public String getPath() {
return "/category/remove";
}
@Override
public Object handle(HttpExchange exchange, Request msg) throws BeaconClientException {
var toRemove = new ArrayList<DataStoreCategory>();
for (UUID uuid : msg.getCategories()) {
var cat = DataStorage.get()
.getStoreCategoryIfPresent(uuid)
.orElseThrow(() -> new BeaconClientException("Unknown category: " + uuid));
if (!DataStorage.get().canDeleteStoreCategory(cat)) {
throw new BeaconClientException("Cannot delete category: " + cat.getName());
}
toRemove.add(cat);
}
for (DataStoreCategory cat : toRemove) {
DataStorage.get().deleteStoreCategory(cat, msg.isRemoveChildrenCategories(), msg.isRemoveContents());
}
return Response.builder().build();
}
@Override
public Object getSynchronizationObject() {
return DataStorage.get();
}
@JacksonizedView on GitHub (pinned to d85ca821ba)
Solutions
- Move or delete all data stores inside the category first, then retry the delete
- Move or remove any child categories under it before deleting the parent
- Check that the UUID is not one of XPipe's built-in/default categories and skip those
- Call canDeleteStoreCategory (or an equivalent listing endpoint) beforehand and only request deletions that pass
Example fix
// before
client.removeCategories(List.of(anyUuid));
// after
for (UUID uuid : uuids) {
if (DataStorage.get().canDeleteStoreCategory(DataStorage.get().getStoreCategoryIfPresent(uuid).orElseThrow())) {
client.removeCategories(List.of(uuid));
}
} Defensive patterns
Strategy: validation
Validate before calling
DataStoreCategory cat = DataStorage.get().getStoreCategoryIfPresent(uuid).orElse(null);
if (cat == null || !DataStorage.get().canDeleteStoreCategory(cat)) {
throw new IllegalStateException("Category " + uuid + " is protected or non-empty");
} Type guard
boolean isDeletable(UUID uuid) {
return DataStorage.get().getStoreCategoryIfPresent(uuid)
.map(c -> DataStorage.get().canDeleteStoreCategory(c))
.orElse(false);
} Try / catch
try {
client.removeCategories(uuids);
} catch (BeaconClientException e) {
if (e.getMessage().startsWith("Cannot delete category")) {
logger.warn("Skipping protected category: " + e.getMessage());
} else throw e;
} Prevention
- Check canDeleteStoreCategory before each delete request
- Empty the category (stores and child categories) before removal
- Never attempt to delete built-in default categories
When it happens
Trigger: Calling the CategoryRemoveExchange beacon endpoint with a category UUID that resolves to a protected or non-empty category: a built-in default category, a category that still contains data stores, or one with child categories.
Common situations: Scripts or integrations that try to wipe all categories before a re-import; deleting the 'Default' or system category; removing a parent category without first moving or deleting its child stores and sub-categories; stale client-side caches listing categories that have become protected.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Unsupported mode: " + msg.getMode().getDisplayName() + ". Su
- File path " + msg.getPath() + " is not absolute
- File " + msg.getPath() + " does not exist
- File path " + msg.getPath() + " is not absolute
- Directory " + msg.getPath().getParent() + " does not exist
AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06).
Data as JSON: /api/errors/07325bb52ee761d1.
Report an issue: GitHub.