Skip to content

Deleting a category fails with a 404 and leaves the dialog spinning #23293

Description

@dcalhoun

Description

Deleting a category from Site Settings → Categories fails on sites that use the wordpress-rs taxonomy path, and the confirmation dialog is left spinning on "Deleting category" forever.

Step-by-step reproduction instructions

  1. Sign in to an Atomic or self-hosted site whose taxonomy requests go through wordpress-rs (site added with an application password, taxonomies_rest_api_migration enabled in the dev menu).
  2. Go to Site Settings → Categories.
  3. Create a new category. It is created successfully — the POST /wp/v2/sites/<id>/categories response carries the new term, e.g. "id": 1447.
  4. Open that category and delete it.

Expected: the category is deleted, or a "Failed to delete category" message appears.

Actual: DELETE /wp/v2/sites/<id>/categories/631?force=true returns 404 rest_term_invalid ("Term does not exist.") — note the id does not match the one returned when the category was created — and the "Deleting category" spinner stays on screen indefinitely.

Screenshots, screen recording, code snippet

POST   .../wp/v2/sites/<id>/categories            201    → { "id": 1447, "count": 0, ... }
GET    .../wp/v2/sites/<id>/categories            200
DELETE .../wp/v2/sites/<id>/categories/631?force=true   404
                                        ^^^
{
  "code": "rest_term_invalid",
  "message": "Term does not exist.",
  "data": { "status": 404 }
}

Environment info

Atomic site, categories served over the wordpress-rs path (taxonomies_rest_api_migration enabled). Both defects are present on trunk.

Please confirm that you have searched existing issues in the repo.

  • Yes

Technical findings

There are two independent defects, both introduced in #22252 (CMM-808, the wordpress-rs taxonomy port).

1. The delete request sends the local database id instead of the remote term id.

TaxonomyRsApiRestClient.deleteTerm identifies the term with term.id:

requestBuilder.terms().delete(
    termEndpointType = termEndpointType,
    termId = term.id.toLong()        // local DB primary key
)

TermModel carries two ids — mId, the local @PrimaryKey row id, and mRemoteTermId, the id the REST API knows. Every sibling client uses the remote identity: TaxonomyXMLRPCClient uses getRemoteTermId(), TaxonomyRestClient uses getSlug(), and updateTerm in this very same class uses term.remoteTermId. Only deleteTerm uses the local one, so the API is asked to delete a term id that belongs to a different term (or to nothing at all) and answers 404 rest_term_invalid.

Creating a category works because creation does not need an id.

The same function has a mirror-image slip in its success path: the confirmation TermModel is built passing term.id.toLong() as the remoteTermId constructor argument, so even a delete that succeeded would hand the store a model whose remote id is really a local row id.

2. A failed deletion emits an event that nothing listens for.

TaxonomyStore reports the two outcomes with different causeOfChange values:

  • success → removeTerm() emits OnTaxonomyChanged(causeOfChange = REMOVE_TERM)
  • failure → emits OnTaxonomyChanged(causeOfChange = DELETE_TERM)

Every consumer handles only REMOVE_TERM — CategoryDetailViewModel, CategoriesListViewModel and SiteSettingsTagListActivity. Nothing subscribes to DELETE_TERM, so the failure is emitted into the void and the InProgress(R.string.deleting_cat) state that CategoryDetailViewModel.deleteCategory() posted is never cleared. The dialog spins indefinitely and the user is never told the deletion failed.

This second defect is client-agnostic: any failed term deletion hangs the dialog, including on WordPress.com and XML-RPC sites. The wrong id simply guarantees the failure every time on the wordpress-rs path.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions