Repository: deepset-ai/haystack

Issue:
Don't retry on 401 in PromptNode
**Describe the bug**
When running a pipeline with a PromptNode, we retry HTTP requests that return a 401 error code. We should not retry these, but instead fail early. 

**Error message**
No error thrown, that is the message on which it is retried
```
WARNING:haystack.utils.reflection:OpenAIError - OpenAI returned an error.                                                                                  │
│ Status code: 401                                                                                                                                           │
│ Response body: {                                                                                                                                           │
│     "error": {                                                                                                                                             │
│         "message": "Incorrect API key provided: **************************************. You can find your API key at https://platform.openai. │
│         "type": "invalid_request_error",                                                                                                                   │
│         "param": null,                                                                                                                                     │
│         "code": "invalid_api_key"                                                                                                                          │
│     }                                                                                                                                                      │
│ }               
```
**Expected behavior**
Don't retry a 401 and raise an exception.

**Additional context**
/

**To Reproduce**
Create a pipeline with an openai PromptNode, let the pipeline run and delete the key in the background. 

**FAQ Check**
- [ ] Have you had a look at [our new FAQ page](https://docs.haystack.deepset.ai/docs/faq)?

**System:**
 - OS: 
 - GPU/CPU:
 - Haystack version (commit or version number): 
 - DocumentStore:
 - Reader:
 - Retriever:



Discussion:

Resolving patch:
diff --git a/haystack/errors.py b/haystack/errors.py
--- a/haystack/errors.py
+++ b/haystack/errors.py
@@ -141,6 +141,16 @@ def __init__(self, message: Optional[str] = None, send_message_in_event: bool =
         super().__init__(message=message, status_code=429, send_message_in_event=send_message_in_event)
 
 
+class OpenAIUnauthorizedError(OpenAIError):
+    """
+    Unauthorized error for OpenAI API (status code 401)
+    See https://platform.openai.com/docs/guides/error-codes/api-errors
+    """
+
+    def __init__(self, message: Optional[str] = None, send_message_in_event: bool = False):
+        super().__init__(message=message, status_code=401, send_message_in_event=send_message_in_event)
+
+
 class CohereError(NodeError):
     """Exception for issues that occur in the Cohere APIs"""
 
@@ -151,6 +161,13 @@ def __init__(
         self.status_code = status_code
 
 
+class CohereUnauthorizedError(CohereError):
+    """Exception for unauthorized access to Cohere APIs"""
+
+    def __init__(self, message: Optional[str] = None, send_message_in_event: bool = False):
+        super().__init__(message=message, status_code=401, send_message_in_event=send_message_in_event)
+
+
 class ImageToTextError(NodeError):
     """Exception for issues that occur in the ImageToText node"""
 
diff --git a/haystack/nodes/retriever/_embedding_encoder.py b/haystack/nodes/retriever/_embedding_encoder.py
--- a/haystack/nodes/retriever/_embedding_encoder.py
+++ b/haystack/nodes/retriever/_embedding_encoder.py
@@ -23,7 +23,7 @@
     HAYSTACK_REMOTE_API_MAX_RETRIES,
     HAYSTACK_REMOTE_API_TIMEOUT_SEC,
 )
-from haystack.errors import CohereError
+from haystack.errors import CohereError, CohereUnauthorizedError
 from haystack.modeling.data_handler.dataloader import NamedDataLoader
 from haystack.modeling.data_handler.dataset import convert_features_to_dataset, flatten_rename
 from haystack.modeling.infer import Inferencer
@@ -388,6 +388,8 @@ def embed(self, model: str, text: List[str]) -> np.ndarray:
         headers = {"Authorization": f"BEARER {self.api_key}", "Content-Type": "application/json"}
         response = requests.request("POST", self.url, headers=headers, data=json.dumps(payload), timeout=COHERE_TIMEOUT)
         res = json.loads(response.text)
+        if response.status_code == 401:
+            raise CohereUnauthorizedError(f"Invalid Cohere API key. {response.text}")
         if response.status_code != 200:
             raise CohereError(response.text, status_code=response.status_code)
         generated_embeddings = [e for e in res["embeddings"]]
diff --git a/haystack/utils/openai_utils.py b/haystack/utils/openai_utils.py
--- a/haystack/utils/openai_utils.py
+++ b/haystack/utils/openai_utils.py
@@ -9,7 +9,7 @@
 
 from transformers import GPT2TokenizerFast
 
-from haystack.errors import OpenAIError, OpenAIRateLimitError
+from haystack.errors import OpenAIError, OpenAIRateLimitError, OpenAIUnauthorizedError
 from haystack.utils.reflection import retry_with_exponential_backoff
 from haystack.environment import (
     HAYSTACK_REMOTE_API_BACKOFF_SEC,
@@ -151,6 +151,8 @@ def openai_request(
         openai_error: OpenAIError
         if response.status_code == 429:
             openai_error = OpenAIRateLimitError(f"API rate limit exceeded: {response.text}")
+        elif response.status_code == 401:
+            openai_error = OpenAIUnauthorizedError(f"API key is invalid: {response.text}")
         else:
             openai_error = OpenAIError(
                 f"OpenAI returned an error.\n"


Test patch: