                             (log/error e "error releasing instance!"))))
       instance)))
 
+(defn- classify-error
+  "Classifies the error responses from the backend into the following vector:
+   - error cause (:client-error, :instance-error or :generic-error),
+   - associated error message, and
+   - the http status code."
+  [error]
+  (let [classification (cond (instance? ExceptionInfo error)
+                             (let [[error-cause message status] (classify-error (ex-cause error))
+                                   error-cause (or (-> error ex-data :error-cause) error-cause)]
+                               [error-cause message status])
+                             (instance? IllegalStateException error)
+                             [:generic-error (.getMessage error) 400]
+                             (instance? EofException error)
+                             [:client-error "Connection unexpectedly closed while streaming request" 400]
+                             (instance? TimeoutException error)
+                             [:instance-error (utils/message :backend-request-timed-out) 504]
+                             :else
+                             [:instance-error (utils/message :backend-request-failed) 502])
+        [error-cause _ _] classification]
+    (log/info (-> error .getClass .getCanonicalName) (.getMessage error) "identified as" error-cause)
+    classification))
+
 (defn- handle-response-error
   "Handles error responses from the backend."
   [error reservation-status-promise service-id request]
-  (let [metrics-map (metrics/retrieve-local-stats-for-service service-id)
-        [promise-value message status]
-        (cond (instance? EofException error)
-              [:client-error "Connection unexpectedly closed while sending request" 400]
-              (instance? TimeoutException error)
-              [:instance-error (utils/message :backend-request-timed-out) 504]
-              :else
-              [:instance-error (utils/message :backend-request-failed) 502])]
-    (deliver reservation-status-promise promise-value)
+  (let [[error-cause message status] (classify-error error)
+        metrics-map (metrics/retrieve-local-stats-for-service service-id)]
+    (deliver reservation-status-promise error-cause)
     (utils/exception->response (ex-info message (assoc metrics-map :status status) error) request)))
 
 (let [min-buffer-size 1024

                    (when (and (utils/non-neg? bytes-read) (.isReady input-stream))
                      (recur)))))
              (catch Throwable throwable
                (complete-request-streaming "there was error in streaming data" throwable)
                (throw throwable))))
          (onError [_ throwable]
            (complete-request-streaming "there was error in request data stream" throwable))))
      (catch Throwable throwable
        (complete-request-streaming "there was error in registering read listener" throwable)))
    body-ch))

(defn- make-http-request

                    (when more-bytes-possibly-available?
                      (recur bytes-streamed' bytes-reported-to-statsd'))))))))
        (catch Exception e
          (log/info e "exception occurred while streaming response for" service-id)
          (meters/mark! stream-exception-meter)
          (let [[error-cause _ _] (classify-error e)]
            (deliver reservation-status-promise error-cause))
          (log/info "sending poison pill to response channel")
          (let [poison-pill-function (poison-pill-fn (cid/get-correlation-id))]
            (when-not (au/timed-offer! resp-chan poison-pill-function 5000)
              (log/info "poison pill offer on response channel timed out!")
              (when-let [output-stream @output-stream-atom]
                (log/info "invoking poison pill directly on output stream")
                (poison-pill-function output-stream)))))
        (finally
          (async/close! resp-chan)
          (async/close! body)
