langgenius / langgenius/dify

Service API audit: six inconsistencies in error handling and event emission

Open
#38,855 3 comments 1 reaction 2 assignees Claimed by @luoshutian3128-cpu View on GitHub
🐞 bug 1.15.0 project#dify
Dominant language
TypeScript
Stars
156k
Forks
24.6k
Avg merge
22h 9m
Merged PRs (30d)
610

Description

### Self Checks

- [x] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [x] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [x] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones.
- [x] I confirm that I am using English to submit this report, otherwise it will be closed.
- [x] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- [x] Please do not modify this template :) and fill in all the required fields.

### Dify version

main (`b03385ffb0`); the same behavior was verified on 1.15.0 during the docs team's Service API audit

### Cloud or Self Hosted

Cloud, Self Hosted (Docker)

### Steps to reproduce

While rewriting the Service API reference, the docs team code-verified every endpoint and found six backend inconsistencies. Each item below lists the trigger, the code location, and expected vs. actual. They are independent; happy to split into separate issues if that helps triage.

**1. Renaming a missing metadata field returns 400 instead of 404**

`PATCH /v1/datasets/{dataset_id}/metadata/{metadata_id}` with a nonexistent `metadata_id`.

`MetadataService.update_metadata_name` raises a bare `ValueError("Metadata not found.")` (`api/services/metadata_service.py`), which the Service API renders as `400 invalid_param`. The same request with a nonexistent `dataset_id` correctly returns `404 not_found`.

Expected: `404 not_found`. Actual: `400 invalid_param` with message `Metadata not found.`

**2. Built-in metadata toggle can report success without doing anything**

`POST /v1/datasets/{dataset_id}/metadata/built-in/{action}` (`DatasetMetadataBuiltInFieldActionServiceApi.post`, `api/controllers/service_api/dataset/metadata.py`). Two facets:

- The `action` parameter is typed `Literal["enable", "disable"]`, but the route accepts any string and the `match` has no default case, so e.g. `POST .../built-in/Enable` returns `{"result": "success"}` having done nothing.
- `enable_built_in_field` / `disable_built_in_field` (`api/services/metadata_service.py`) wrap their whole body in `except Exception: logger.exception(...)`, so a failed toggle (for example lock contention) also returns success.

Expected: unknown actions rejected with 400; failures surfaced as errors. Actual: `200 {"result": "success"}` in both cases.

**3. Rate limit surfaces as 500 on completion-messages but 429 on chat-messages**

Exhaust the workspace rate limit, then call `POST /v1/completion-messages`.

In `api/controllers/service_api/app/completion.py`, only `ChatApi.post` catches `InvokeRateLimitError` and maps it to 429; `CompletionApi.post` has no such handler, so the same condition falls through to `500 internal_server_error`.

Expected: `429 rate_limit_error`, consistent with chat. Actual: `500 internal_server_error`.

**4. Oversized upload returns 413 with an empty message**

`POST /v1/files/upload` with a file over the size limit.

`api/services/file_service.py:82` raises `FileTooLargeError` with no arguments, and `BaseServiceError.__init__` defaults `description=""`, so the 413 response body carries `"message": ""` and clients get no size context.

Expected: a message naming the limit (like the audio endpoints produce). Actual: empty `message`.

**5. Citations & Attributions toggle has no effect for Agent apps**

Enable Citations & Attributions in an Agent app's chat features, run a query that retrieves from knowledge.

The toggle persists to the app config, but nothing under `api/core/app/apps/agent_app/` emits `QueueRetrieverResourcesEvent` (only the advanced-chat pipeline and the workflow runner do), so `retriever_resources` never appears in `message_end` metadata for Agent apps.

Expected: citations in `message_end` metadata, as for Chatbot and Chatflow. Actual: the toggle is a no-op.

**6. Stream-events path variable is named `task_id` but holds the workflow run ID**

`GET /v1/workflow/{task_id}/events` (`api/controllers/service_api/app/workflow_events.py:60`).

The handler treats the value as the workflow run ID (`get_workflow_run_by_id_and_tenant_id(run_id=task_id)`). The name collides with the real `task_id` concept used by the stop endpoints, which is a different identifier. Purely a naming issue, but it misleads integrators reading the route; the API docs already describe this parameter as `workflow_run_id`.

### ❌ Actual Behavior

See the per-item expected vs. actual above.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.