feat: return correct dify-plugin-daemon error message (#34171)

This commit is contained in:
wangxiaolei
2026-03-27 14:02:29 +08:00
committed by GitHub
parent 2394e45ec7
commit 689761bfcb
4 changed files with 68 additions and 61 deletions

View File

@@ -200,7 +200,7 @@ class PluginDebuggingKeyApi(Resource):
"port": dify_config.PLUGIN_REMOTE_INSTALL_PORT,
}
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/list")
@@ -215,7 +215,7 @@ class PluginListApi(Resource):
try:
plugins_with_total = PluginService.list_with_total(tenant_id, args.page, args.page_size)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder({"plugins": plugins_with_total.list, "total": plugins_with_total.total})
@@ -232,7 +232,7 @@ class PluginListLatestVersionsApi(Resource):
try:
versions = PluginService.list_latest_versions(args.plugin_ids)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder({"versions": versions})
@@ -251,7 +251,7 @@ class PluginListInstallationsFromIdsApi(Resource):
try:
plugins = PluginService.list_installations_from_ids(tenant_id, args.plugin_ids)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder({"plugins": plugins})
@@ -266,7 +266,7 @@ class PluginIconApi(Resource):
try:
icon_bytes, mimetype = PluginService.get_asset(args.tenant_id, args.filename)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
icon_cache_max_age = dify_config.TOOL_ICON_CACHE_MAX_AGE
return send_file(io.BytesIO(icon_bytes), mimetype=mimetype, max_age=icon_cache_max_age)
@@ -286,7 +286,7 @@ class PluginAssetApi(Resource):
binary = PluginService.extract_asset(tenant_id, args.plugin_unique_identifier, args.file_name)
return send_file(io.BytesIO(binary), mimetype="application/octet-stream")
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/upload/pkg")
@@ -303,7 +303,7 @@ class PluginUploadFromPkgApi(Resource):
try:
response = PluginService.upload_pkg(tenant_id, content)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder(response)
@@ -323,7 +323,7 @@ class PluginUploadFromGithubApi(Resource):
try:
response = PluginService.upload_pkg_from_github(tenant_id, args.repo, args.version, args.package)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder(response)
@@ -361,7 +361,7 @@ class PluginInstallFromPkgApi(Resource):
try:
response = PluginService.install_from_local_pkg(tenant_id, args.plugin_unique_identifiers)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder(response)
@@ -387,7 +387,7 @@ class PluginInstallFromGithubApi(Resource):
args.package,
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder(response)
@@ -407,7 +407,7 @@ class PluginInstallFromMarketplaceApi(Resource):
try:
response = PluginService.install_from_marketplace_pkg(tenant_id, args.plugin_unique_identifiers)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder(response)
@@ -433,7 +433,7 @@ class PluginFetchMarketplacePkgApi(Resource):
}
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/fetch-manifest")
@@ -453,7 +453,7 @@ class PluginFetchManifestApi(Resource):
{"manifest": PluginService.fetch_plugin_manifest(tenant_id, args.plugin_unique_identifier).model_dump()}
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/tasks")
@@ -471,7 +471,7 @@ class PluginFetchInstallTasksApi(Resource):
try:
return jsonable_encoder({"tasks": PluginService.fetch_install_tasks(tenant_id, args.page, args.page_size)})
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/tasks/<task_id>")
@@ -486,7 +486,7 @@ class PluginFetchInstallTaskApi(Resource):
try:
return jsonable_encoder({"task": PluginService.fetch_install_task(tenant_id, task_id)})
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/tasks/<task_id>/delete")
@@ -501,7 +501,7 @@ class PluginDeleteInstallTaskApi(Resource):
try:
return {"success": PluginService.delete_install_task(tenant_id, task_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/tasks/delete_all")
@@ -516,7 +516,7 @@ class PluginDeleteAllInstallTaskItemsApi(Resource):
try:
return {"success": PluginService.delete_all_install_task_items(tenant_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/tasks/<task_id>/delete/<path:identifier>")
@@ -531,7 +531,7 @@ class PluginDeleteInstallTaskItemApi(Resource):
try:
return {"success": PluginService.delete_install_task_item(tenant_id, task_id, identifier)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/upgrade/marketplace")
@@ -553,7 +553,7 @@ class PluginUpgradeFromMarketplaceApi(Resource):
)
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/upgrade/github")
@@ -580,7 +580,7 @@ class PluginUpgradeFromGithubApi(Resource):
)
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/uninstall")
@@ -598,7 +598,7 @@ class PluginUninstallApi(Resource):
try:
return {"success": PluginService.uninstall(tenant_id, args.plugin_installation_id)}
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
@console_ns.route("/workspaces/current/plugin/permission/change")
@@ -674,7 +674,7 @@ class PluginFetchDynamicSelectOptionsApi(Resource):
provider_type=args.provider_type,
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder({"options": options})
@@ -705,7 +705,7 @@ class PluginFetchDynamicSelectOptionsWithCredentialsApi(Resource):
credentials=args.credentials,
)
except PluginDaemonClientSideError as e:
raise ValueError(e)
return {"code": "plugin_error", "message": e.description}, 400
return jsonable_encoder({"options": options})

View File

@@ -13,6 +13,7 @@ from core.plugin.endpoint.exc import EndpointSetupFailedError
from core.plugin.entities.plugin_daemon import PluginDaemonBasicResponse, PluginDaemonError, PluginDaemonInnerError
from core.plugin.impl.exc import (
PluginDaemonBadRequestError,
PluginDaemonClientSideError,
PluginDaemonInternalServerError,
PluginDaemonNotFoundError,
PluginDaemonUnauthorizedError,
@@ -235,7 +236,10 @@ class BasePluginClient:
response.raise_for_status()
except httpx.HTTPStatusError as e:
logger.exception("Failed to request plugin daemon, status: %s, url: %s", e.response.status_code, path)
raise e
if e.response.status_code < 500:
raise PluginDaemonClientSideError(description=str(e))
else:
raise PluginDaemonInternalServerError(description=str(e))
except Exception as e:
msg = f"Failed to request plugin daemon, url: {path}"
logger.exception("Failed to request plugin daemon, url: %s", path)

View File

@@ -90,8 +90,8 @@ class TestPluginListLatestVersionsApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginDebuggingKeyApi:
@@ -120,8 +120,8 @@ class TestPluginDebuggingKeyApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginListApi:
@@ -202,8 +202,9 @@ class TestPluginUploadFromPkgApi:
patch("controllers.console.workspace.plugin.dify_config.PLUGIN_MAX_PACKAGE_SIZE", 0),
patch("controllers.console.workspace.plugin.PluginService.upload_pkg") as upload_pkg_mock,
):
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
method(api)
assert "File size exceeds the maximum allowed size" in str(exc_info.value)
upload_pkg_mock.assert_not_called()
@@ -365,8 +366,8 @@ class TestPluginListInstallationsFromIdsApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginUploadFromGithubApi:
@@ -401,8 +402,8 @@ class TestPluginUploadFromGithubApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginUploadFromBundleApi:
@@ -449,8 +450,9 @@ class TestPluginUploadFromBundleApi:
patch("controllers.console.workspace.plugin.dify_config.PLUGIN_MAX_BUNDLE_SIZE", 0),
patch("controllers.console.workspace.plugin.PluginService.upload_bundle") as upload_bundle_mock,
):
with pytest.raises(ValueError):
with pytest.raises(ValueError) as exc_info:
method(api)
assert "File size exceeds the maximum allowed size" in str(exc_info.value)
upload_bundle_mock.assert_not_called()
@@ -495,8 +497,8 @@ class TestPluginInstallFromGithubApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginInstallFromMarketplaceApi:
@@ -532,8 +534,8 @@ class TestPluginInstallFromMarketplaceApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginFetchMarketplacePkgApi:
@@ -562,8 +564,8 @@ class TestPluginFetchMarketplacePkgApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginFetchManifestApi:
@@ -595,8 +597,8 @@ class TestPluginFetchManifestApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginFetchInstallTasksApi:
@@ -625,8 +627,8 @@ class TestPluginFetchInstallTasksApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginFetchInstallTaskApi:
@@ -655,8 +657,8 @@ class TestPluginFetchInstallTaskApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api, "t")
result = method(api, "t")
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginDeleteInstallTaskApi:
@@ -685,8 +687,8 @@ class TestPluginDeleteInstallTaskApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api, "t")
result = method(api, "t")
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginDeleteAllInstallTaskItemsApi:
@@ -717,8 +719,8 @@ class TestPluginDeleteAllInstallTaskItemsApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginDeleteInstallTaskItemApi:
@@ -747,8 +749,8 @@ class TestPluginDeleteInstallTaskItemApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api, "task1", "item1")
result = method(api, "task1", "item1")
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginUpgradeFromMarketplaceApi:
@@ -790,8 +792,8 @@ class TestPluginUpgradeFromMarketplaceApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginUpgradeFromGithubApi:
@@ -839,8 +841,8 @@ class TestPluginUpgradeFromGithubApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginFetchDynamicSelectOptionsWithCredentialsApi:
@@ -894,8 +896,8 @@ class TestPluginFetchDynamicSelectOptionsWithCredentialsApi:
side_effect=PluginDaemonClientSideError("error"),
),
):
with pytest.raises(ValueError):
method(api)
result = method(api)
assert result == ({"code": "plugin_error", "message": "error"}, 400)
class TestPluginChangePreferencesApi:

View File

@@ -26,6 +26,7 @@ from core.plugin.entities.plugin_daemon import (
from core.plugin.impl.base import BasePluginClient
from core.plugin.impl.exc import (
PluginDaemonBadRequestError,
PluginDaemonClientSideError,
PluginDaemonInternalServerError,
PluginDaemonNotFoundError,
PluginDaemonUnauthorizedError,
@@ -557,7 +558,7 @@ class TestPluginRuntimeErrorHandling:
with patch("httpx.request", return_value=mock_response, autospec=True):
# Act & Assert
with pytest.raises(httpx.HTTPStatusError):
with pytest.raises(PluginDaemonInternalServerError):
plugin_client._request_with_plugin_daemon_response("GET", "plugin/test-tenant/test", bool)
def test_empty_data_response_error(self, plugin_client, mock_config):
@@ -1808,8 +1809,8 @@ class TestPluginInstallerAdvanced:
mock_response.raise_for_status = raise_for_status
with patch("httpx.request", return_value=mock_response, autospec=True):
# Act & Assert - Should raise HTTPStatusError for 404
with pytest.raises(httpx.HTTPStatusError):
# Act & Assert - Should raise PluginDaemonClientSideError for 404
with pytest.raises(PluginDaemonClientSideError):
installer.fetch_plugin_readme("test-tenant", "test-org/test-plugin", "en")
def test_list_plugins_with_pagination(self, installer, mock_config):