diff --git a/HISTORY.rst b/HISTORY.rst
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_SElTVE9SWS5yc3Q=..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_SElTVE9SWS5yc3Q= 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -47,6 +47,8 @@
 
 - preview: fix metadata not being passed to rendering engine
 
+- render: return detailed errors
+
 0.8.0 (2024-12-09)
 ==================
 
diff --git a/lib/render.go b/lib/render.go
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_bGliL3JlbmRlci5nbw==..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_bGliL3JlbmRlci5nbw== 100644
--- a/lib/render.go
+++ b/lib/render.go
@@ -197,8 +197,10 @@
 		for _, record := range data {
 			var doc models.Document
 
+			result = append(result, &doc)
+
 			w := rendering.NewDocumentWriter(&doc)
 			if err := pipeline.Render(ctx, w, record); err != nil {
 				pipelineTimer.ObserveDurationWithLabelValues(
 					templateLanguage, fromType, toType, "failure")
 
@@ -200,8 +202,12 @@
 			w := rendering.NewDocumentWriter(&doc)
 			if err := pipeline.Render(ctx, w, record); err != nil {
 				pipelineTimer.ObserveDurationWithLabelValues(
 					templateLanguage, fromType, toType, "failure")
 
+				if errors.Is(err, ErrInvalidInput) {
+					return nil, models.NewRenderFailedError(err, result)
+				}
+
 				return nil, err
 			}
 
@@ -212,8 +218,6 @@
 					})
 				}
 			}
-
-			result = append(result, &doc)
 		}
 	}
 
@@ -228,8 +232,10 @@
 		if len(pipeline) != 0 {
 			var output models.Document
 
+			result = append(result, &output)
+
 			reader := rendering.NewDocumentReader(document)
 			writer := rendering.NewDocumentWriter(&output)
 
 			userContext := SetUsername(ctx, username)
 			if err := pipeline.Render(userContext, writer, reader); err != nil {
@@ -231,8 +237,12 @@
 			reader := rendering.NewDocumentReader(document)
 			writer := rendering.NewDocumentWriter(&output)
 
 			userContext := SetUsername(ctx, username)
 			if err := pipeline.Render(userContext, writer, reader); err != nil {
+				if errors.Is(err, ErrInvalidInput) {
+					return nil, models.NewRenderFailedError(err, result)
+				}
+
 				return nil, err
 			}
 
@@ -243,8 +253,6 @@
 					})
 				}
 			}
-
-			result = append(result, &output)
 		} else {
 			for _, cb := range r.onDocRender {
 				if err := cb(ctx, &db, document); err != nil {
diff --git a/models/render_failed_doc_details.go b/models/render_failed_doc_details.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_bW9kZWxzL3JlbmRlcl9mYWlsZWRfZG9jX2RldGFpbHMuZ28=
--- /dev/null
+++ b/models/render_failed_doc_details.go
@@ -0,0 +1,101 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+	"strconv"
+
+	"github.com/go-openapi/errors"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+)
+
+// RenderFailedDocDetails render failed doc details
+//
+// swagger:model render-failed-doc-details
+type RenderFailedDocDetails struct {
+
+	// metadata
+	Metadata Metadata `json:"metadata,omitempty"`
+
+	// A list of errors that occured during rendering
+	RenderErrors []RenderError `json:"render-errors"`
+
+	// The document mimetype
+	Type string `json:"type,omitempty"`
+}
+
+// Validate validates this render failed doc details
+func (m *RenderFailedDocDetails) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateMetadata(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateRenderErrors(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *RenderFailedDocDetails) validateMetadata(formats strfmt.Registry) error {
+
+	if swag.IsZero(m.Metadata) { // not required
+		return nil
+	}
+
+	if err := m.Metadata.Validate(formats); err != nil {
+		if ve, ok := err.(*errors.Validation); ok {
+			return ve.ValidateName("metadata")
+		}
+		return err
+	}
+
+	return nil
+}
+
+func (m *RenderFailedDocDetails) validateRenderErrors(formats strfmt.Registry) error {
+
+	if swag.IsZero(m.RenderErrors) { // not required
+		return nil
+	}
+
+	for i := 0; i < len(m.RenderErrors); i++ {
+
+		if err := m.RenderErrors[i].Validate(formats); err != nil {
+			if ve, ok := err.(*errors.Validation); ok {
+				return ve.ValidateName("render-errors" + "." + strconv.Itoa(i))
+			}
+			return err
+		}
+
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *RenderFailedDocDetails) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *RenderFailedDocDetails) UnmarshalBinary(b []byte) error {
+	var res RenderFailedDocDetails
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
diff --git a/models/render_failed_error.go b/models/render_failed_error.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_bW9kZWxzL3JlbmRlcl9mYWlsZWRfZXJyb3IuZ28=
--- /dev/null
+++ b/models/render_failed_error.go
@@ -0,0 +1,83 @@
+// Code generated by go-swagger; DO NOT EDIT.
+
+package models
+
+// This file was generated by the swagger tool.
+// Editing this file might prove futile when you re-run the swagger generate command
+
+import (
+	"strconv"
+
+	"github.com/go-openapi/errors"
+	"github.com/go-openapi/strfmt"
+	"github.com/go-openapi/swag"
+)
+
+// RenderFailedError Rendering failure
+//
+// swagger:model render-failed-error
+type RenderFailedError struct {
+
+	// Detailed errors reported by the rendering engines for each document
+	Details []*RenderFailedDocDetails `json:"details"`
+
+	// The main reason why the rendering failed
+	Message string `json:"message,omitempty"`
+}
+
+// Validate validates this render failed error
+func (m *RenderFailedError) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validateDetails(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *RenderFailedError) validateDetails(formats strfmt.Registry) error {
+
+	if swag.IsZero(m.Details) { // not required
+		return nil
+	}
+
+	for i := 0; i < len(m.Details); i++ {
+		if swag.IsZero(m.Details[i]) { // not required
+			continue
+		}
+
+		if m.Details[i] != nil {
+			if err := m.Details[i].Validate(formats); err != nil {
+				if ve, ok := err.(*errors.Validation); ok {
+					return ve.ValidateName("details" + "." + strconv.Itoa(i))
+				}
+				return err
+			}
+		}
+
+	}
+
+	return nil
+}
+
+// MarshalBinary interface implementation
+func (m *RenderFailedError) MarshalBinary() ([]byte, error) {
+	if m == nil {
+		return nil, nil
+	}
+	return swag.WriteJSON(m)
+}
+
+// UnmarshalBinary interface implementation
+func (m *RenderFailedError) UnmarshalBinary(b []byte) error {
+	var res RenderFailedError
+	if err := swag.ReadJSON(b, &res); err != nil {
+		return err
+	}
+	*m = res
+	return nil
+}
diff --git a/models/render_failed_error_extra.go b/models/render_failed_error_extra.go
new file mode 100644
index 0000000000000000000000000000000000000000..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_bW9kZWxzL3JlbmRlcl9mYWlsZWRfZXJyb3JfZXh0cmEuZ28=
--- /dev/null
+++ b/models/render_failed_error_extra.go
@@ -0,0 +1,20 @@
+package models
+
+func NewRenderFailedError(err error, docs []*Document) *RenderFailedError {
+	r := RenderFailedError{
+		Message: err.Error(),
+		Details: make([]*RenderFailedDocDetails, len(docs)),
+	}
+
+	for i, doc := range docs {
+		r.Details[i].Type = doc.Type
+		r.Details[i].Metadata = doc.Metadata
+		r.Details[i].RenderErrors = doc.RenderErrors
+	}
+
+	return &r
+}
+
+func (err *RenderFailedError) Error() string {
+	return err.Message
+}
diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_cmVzdGFwaS9lbWJlZGRlZF9zcGVjLmdv..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_cmVzdGFwaS9lbWJlZGRlZF9zcGVjLmdv 100644
--- a/restapi/embedded_spec.go
+++ b/restapi/embedded_spec.go
@@ -250,6 +250,12 @@
               }
             }
           },
+          "400": {
+            "description": "A blocking rendering error occured",
+            "schema": {
+              "$ref": "#/definitions/render-failed-error"
+            }
+          },
           "401": {
             "$ref": "#/responses/unauthorized"
           },
@@ -1145,6 +1151,43 @@
       },
       "x-isnullable": false
     },
+    "render-failed-doc-details": {
+      "type": "object",
+      "properties": {
+        "metadata": {
+          "$ref": "#/definitions/metadata"
+        },
+        "render-errors": {
+          "description": "A list of errors that occured during rendering",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/render-error"
+          }
+        },
+        "type": {
+          "description": "The document mimetype",
+          "type": "string",
+          "example": "text/html"
+        }
+      }
+    },
+    "render-failed-error": {
+      "description": "Rendering failure",
+      "type": "object",
+      "properties": {
+        "details": {
+          "description": "Detailed errors reported by the rendering engines for each document",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/render-failed-doc-details"
+          }
+        },
+        "message": {
+          "description": "The main reason why the rendering failed",
+          "type": "string"
+        }
+      }
+    },
     "render-request": {
       "type": "object",
       "properties": {
@@ -1640,6 +1683,12 @@
               }
             }
           },
+          "400": {
+            "description": "A blocking rendering error occured",
+            "schema": {
+              "$ref": "#/definitions/render-failed-error"
+            }
+          },
           "401": {
             "description": "Unauthorized",
             "schema": {
@@ -2664,6 +2713,43 @@
       },
       "x-isnullable": false
     },
+    "render-failed-doc-details": {
+      "type": "object",
+      "properties": {
+        "metadata": {
+          "$ref": "#/definitions/metadata"
+        },
+        "render-errors": {
+          "description": "A list of errors that occured during rendering",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/render-error"
+          }
+        },
+        "type": {
+          "description": "The document mimetype",
+          "type": "string",
+          "example": "text/html"
+        }
+      }
+    },
+    "render-failed-error": {
+      "description": "Rendering failure",
+      "type": "object",
+      "properties": {
+        "details": {
+          "description": "Detailed errors reported by the rendering engines for each document",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/render-failed-doc-details"
+          }
+        },
+        "message": {
+          "description": "The main reason why the rendering failed",
+          "type": "string"
+        }
+      }
+    },
     "render-request": {
       "type": "object",
       "properties": {
diff --git a/restapi/handlers/render.go b/restapi/handlers/render.go
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_cmVzdGFwaS9oYW5kbGVycy9yZW5kZXIuZ28=..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_cmVzdGFwaS9oYW5kbGVycy9yZW5kZXIuZ28= 100644
--- a/restapi/handlers/render.go
+++ b/restapi/handlers/render.go
@@ -40,5 +40,7 @@
 		ctx, principal.Subject, template, data, document, metadata, toType,
 		principal.EnabledRequestLogging,
 	)
-	if errors.Is(err, redner.ErrInvalidInput) {
+
+	switch {
+	case errors.Is(err, redner.ErrInvalidInput):
 		return nil, utils.HTTPBadRequest(err)
@@ -44,5 +46,5 @@
 		return nil, utils.HTTPBadRequest(err)
-	} else if err != nil {
+	case err != nil:
 		return nil, err
 	}
 
@@ -76,6 +78,11 @@
 			return r
 		}
 
+		var renderFailedError *models.RenderFailedError
+		if errors.As(err, &renderFailedError) {
+			return op.NewRenderBadRequest().WithPayload(renderFailedError)
+		}
+
 		if params.HTTPRequest.Context().Err() != nil {
 			return op.NewRenderDefault(499)
 		}
diff --git a/restapi/operations/rendering/render_responses.go b/restapi/operations/rendering/render_responses.go
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_cmVzdGFwaS9vcGVyYXRpb25zL3JlbmRlcmluZy9yZW5kZXJfcmVzcG9uc2VzLmdv..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_cmVzdGFwaS9vcGVyYXRpb25zL3JlbmRlcmluZy9yZW5kZXJfcmVzcG9uc2VzLmdv 100644
--- a/restapi/operations/rendering/render_responses.go
+++ b/restapi/operations/rendering/render_responses.go
@@ -60,6 +60,50 @@
 	}
 }
 
+// RenderBadRequestCode is the HTTP code returned for type RenderBadRequest
+const RenderBadRequestCode int = 400
+
+/*RenderBadRequest A blocking rendering error occured
+
+swagger:response renderBadRequest
+*/
+type RenderBadRequest struct {
+
+	/*
+	  In: Body
+	*/
+	Payload *models.RenderFailedError `json:"body,omitempty"`
+}
+
+// NewRenderBadRequest creates RenderBadRequest with default headers values
+func NewRenderBadRequest() *RenderBadRequest {
+
+	return &RenderBadRequest{}
+}
+
+// WithPayload adds the payload to the render bad request response
+func (o *RenderBadRequest) WithPayload(payload *models.RenderFailedError) *RenderBadRequest {
+	o.Payload = payload
+	return o
+}
+
+// SetPayload sets the payload to the render bad request response
+func (o *RenderBadRequest) SetPayload(payload *models.RenderFailedError) {
+	o.Payload = payload
+}
+
+// WriteResponse to the client
+func (o *RenderBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
+
+	rw.WriteHeader(400)
+	if o.Payload != nil {
+		payload := o.Payload
+		if err := producer.Produce(rw, payload); err != nil {
+			panic(err) // let the recovery middleware deal with this
+		}
+	}
+}
+
 // RenderUnauthorizedCode is the HTTP code returned for type RenderUnauthorized
 const RenderUnauthorizedCode int = 401
 
diff --git a/swagger.yaml b/swagger.yaml
index 7bca2c685c50b7e1cec5c9345a2e2546dee287d8_c3dhZ2dlci55YW1s..2c15f4f87eb5e06b5dbfa8a3b94e70192938e3a8_c3dhZ2dlci55YW1s 100644
--- a/swagger.yaml
+++ b/swagger.yaml
@@ -632,6 +632,10 @@
               $ref: "#/definitions/document"
         401:
           $ref: '#/responses/unauthorized'
+        400:
+          description: A blocking rendering error occured
+          schema:
+            $ref: '#/definitions/render-failed-error'
         default:
           $ref: '#/responses/default'
 
@@ -718,6 +722,34 @@
       message:
         type: string
 
+  render-failed-doc-details:
+    type: object
+    properties:
+      type:
+        type: string
+        description: The document mimetype
+        example: text/html
+      metadata:
+        $ref: "#/definitions/metadata"
+      render-errors:
+        type: array
+        description: A list of errors that occured during rendering
+        items:
+          $ref: "#/definitions/render-error"
+
+  render-failed-error:
+    type: object
+    description: Rendering failure
+    properties:
+      message:
+        type: string
+        description: The main reason why the rendering failed
+      details:
+        type: array
+        description: Detailed errors reported by the rendering engines for each document
+        items:
+          $ref: "#/definitions/render-failed-doc-details"
+
   credentials:
     type: object
     description: Credentials for authentication