Skip to content

Docs document a work-item relation removal endpoint that does not exist in the API #9584

Description

@mggarofalo

The API reference documents an endpoint for removing a work-item relation:

https://developers.plane.so/api-reference/work-item-relations/remove-work-item-relation

POST /api/v1/workspaces/{workspace_slug}/projects/{project_id}/work-items/{work_item_id}/relations/remove/
{"related_issue": "<uuid>"}

That route is not registered anywhere in this repository, so it returns 404 on self-hosted instances.

What the code actually has

apps/api/plane/api/urls/work_item.py registers exactly one relations route:

path(
    "workspaces/<str:slug>/projects/<uuid:project_id>/work-items/<uuid:issue_id>/relations/",
    IssueRelationListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
    name="work-item-relation-list",
),

IssueRelationListCreateAPIEndpoint (apps/api/plane/api/views/issue.py) defines get and post and nothing else. relations/remove does not appear anywhere in the tree.

Curiously, the serializer for the documented request body does exist — IssueRelationRemoveSerializer in apps/api/plane/api/serializers/issue.py, with a related_issue UUID field and the exact help text from the docs page. It is not imported anywhere, not exported from serializers/__init__.py, and not referenced by any view. It looks like the request shape was ported over but the endpoint that would consume it was not.

There is a relations/remove/ route on commit 801b3315d, but it was never merged (only on fix/comments-mention, in no tag), and it uses the issues/ prefix rather than the work-items/ prefix the docs advertise.

Versions checked

The relations list/create route arrived in #8763 and first shipped in v1.3.0. I checked v0.28.0, v1.0.0, v1.1.0, v1.2.0, v1.2.3, v1.3.0, v1.3.1, v1.4.0 and v1.4.1 — the remove sub-route is in none of them.

Reproduction

Against a self-hosted instance, with a personal API key:

# works — creates the relation, and the inverse edge
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  "$HOST/api/v1/workspaces/$WS/projects/$PROJ/work-items/$ID/relations/" \
  -d '{"relation_type": "blocked_by", "issues": ["'"$OTHER"'"]}'

# works — returns the relation-type-keyed object
curl -H "X-API-Key: $KEY" \
  "$HOST/api/v1/workspaces/$WS/projects/$PROJ/work-items/$ID/relations/"

# 404 {"error": "Page not found."}
curl -X POST -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
  "$HOST/api/v1/workspaces/$WS/projects/$PROJ/work-items/$ID/relations/remove/" \
  -d '{"related_issue": "'"$OTHER"'"}'

Impact

Relations can be created and read through the public API but never removed, so an integration can build a dependency graph and cannot correct it. The only working path is the internal app API:

POST /api/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/remove-relation/

which uses BaseSessionAuthentication and so rejects an X-API-Key. That makes it unusable from a CLI or server-to-server integration.

Either implementation would resolve this — please just let us know which, so integrations can target the right one:

  1. Add the documented route to the public API (IssueRelationRemoveSerializer is already written).
  2. Correct the docs to state that removal is not available in v1, so nobody builds against a page that describes an endpoint that isn't there.

One incidental note

If the endpoint is implemented by reusing IssueRelationViewSet.remove_relation (apps/api/plane/app/views/issue/relation.py), that handler calls .first() and then .delete() with no None check, so removing a relation that doesn't exist raises AttributeError → HTTP 500 rather than a 404.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions