diff --git a/client/qiskit_serverless/utils/json.py b/client/qiskit_serverless/utils/json.py index abfa7dd26..dffddd703 100644 --- a/client/qiskit_serverless/utils/json.py +++ b/client/qiskit_serverless/utils/json.py @@ -139,17 +139,26 @@ def safe_json_request( if error_message: raise QiskitServerlessException(error_message) - if response is not None and not response.ok: - raise QiskitServerlessException( - format_err_msg( - response.status_code, - str(response.text), - ) - ) - decoding_error_message: Optional[str] = None try: json_data = json.loads(response.text) + if response is not None and not response.ok: + # When response is not ok, the expected json + # response is a dictionary with a field + # that contains the error message. The key + # varies between messages, so appending all + # values to a string allows to capture all + # potential messages. + error_msg = "" + for error_string in json_data.values(): + error_msg += str(error_string) + + raise QiskitServerlessException( + format_err_msg( + response.status_code, + str(error_msg), + ) + ) except json.JSONDecodeError as json_error: decoding_error_message = format_err_msg( ErrorCodes.JSON1001, diff --git a/gateway/api/views/programs.py b/gateway/api/views/programs.py index ad1d8f9f7..f8468c582 100644 --- a/gateway/api/views/programs.py +++ b/gateway/api/views/programs.py @@ -3,6 +3,7 @@ Version views inherit from the different views. """ + import logging import os @@ -290,15 +291,32 @@ def get_by_title(self, request, title): title=function_title, provider_name=provider_name, ) + if function is None: + return Response( + { + "message": ( + f"Program '{function_title}' for provider '{provider_name}' " + "was not found or you do not have permission to view it." + ) + }, + status=status.HTTP_404_NOT_FOUND, + ) else: function = self.function_repository.get_user_function( author=author, title=function_title ) + if function is None: + return Response( + { + "message": ( + f"User program '{function_title}' was not found or " + "you do not have permission to view it." + ) + }, + status=status.HTTP_404_NOT_FOUND, + ) - if function: - return Response(self.get_serializer(function).data) - - return Response(status=404) + return Response(self.get_serializer(function).data) # This end-point is deprecated and we need to confirm if we can remove it @_trace diff --git a/releasenotes/notes/improve-exception-404-9815392d005df623.yaml b/releasenotes/notes/improve-exception-404-9815392d005df623.yaml new file mode 100644 index 000000000..4de5a3f85 --- /dev/null +++ b/releasenotes/notes/improve-exception-404-9815392d005df623.yaml @@ -0,0 +1,17 @@ +--- +fixes: + - | + Fixed a bug in the json decoder that would crop the response details to the first character when + gateway errors were raised. This utility now forwards the full error message to the raised + ``QiskitServerlessException``. + + - | + Fixed error handling when a function is not found. Instead of providing a + bare 404 error, the ``QiskitServerlessException`` now contains a more meaningful error message:: + + QiskitServerlessException: + | Message: Http bad request. + | Code: 404 + | Details: User program 'my-program' was not found or you do not have permission to view it. + + Fixes https://github.com/Qiskit/qiskit-serverless/issues/1773. diff --git a/tests/docker/test_docker.py b/tests/docker/test_docker.py index ace8d02ce..8619a109c 100644 --- a/tests/docker/test_docker.py +++ b/tests/docker/test_docker.py @@ -178,9 +178,7 @@ def test_function_blocked_dependency(self, serverless_client: ServerlessClient): def exceptionCheck(e: QiskitServerlessException): code_index = str(e).find("Code: 400") - details_index = str(e).find( - f"non_field_errors: Dependency `{dependency}` is not allowed" - ) + details_index = str(e).find(f"Dependency `{dependency}` is not allowed") return code_index > 0 and details_index > 0 with raises(QiskitServerlessException, check=exceptionCheck): @@ -390,3 +388,26 @@ def test_get_filtered_jobs(self, serverless_client: ServerlessClient): succeeded_jobs = serverless_client.jobs(status="SUCCEEDED") assert len(succeeded_jobs) >= 3 + + def test_wrong_function_name(self, serverless_client: ServerlessClient): + """Integration test for retrieving a function that isn't accessible.""" + + arguments_function = QiskitFunction( + title="pattern-with-arguments", + entrypoint="pattern_with_arguments.py", + working_dir=resources_path, + ) + + expected_message = ( + "\n| Message: Http bad request.\n" + "| Code: 404\n" + "| Details: User program 'wrong-title' was not found or you do not " + "have permission to view it." + ) + + serverless_client.upload(arguments_function) + + with raises(QiskitServerlessException) as exc_info: + serverless_client.function("wrong-title") + + assert str(exc_info.value) == expected_message