herodot icon indicating copy to clipboard operation
herodot copied to clipboard

WriteErrorCode function always returns 500 code inside JSON payload

Open davidspiess opened this issue 11 months ago • 3 comments

Preflight checklist

Describe the bug

I noticed by using the (h *JSONWriter) WriteErrorCode function, that the header is correctly set, but the JSON response returns a 500 status code and message.

Is this a bug or am i interpreting the purpose of the function wrong?

Reproducing the bug

h.d.Writer().WriteErrorCode(w, r, 401, errors.New("error"))

Response: Image

Version

v0.10.2

On which operating system are you observing this issue?

Linux

In which environment are you deploying?

None

Additional Context

My workaround right now:

h.d.Writer().WriteError(w, r, herodot.DefaultError{
	CodeField:  401,
	ErrorField: errors.New("error"),
})

davidspiess avatar Mar 06 '25 08:03 davidspiess

func defaultJSONErrorEnhancer(r *http.Request, err error) interface{} {
	if e, ok := err.(ErrorEnhancer); ok {
		return e.EnhanceJSONError()
	}
	return &ErrorContainer{Error: ToDefaultError(err, r.Header.Get("X-Request-ID"))}
}
func ToDefaultError(err error, requestID string) *DefaultError {
	de := &DefaultError{
		RIDField:     requestID,
		CodeField:    http.StatusInternalServerError,
		DetailsField: map[string]interface{}{},
		ErrorField:   err.Error(),
	}
	de.Wrap(err)

	if c := ReasonCarrier(nil); stderr.As(err, &c) {
		de.ReasonField = c.Reason()
	}
	if c := RequestIDCarrier(nil); stderr.As(err, &c) && c.RequestID() != "" {
		de.RIDField = c.RequestID()
	}
	if c := DetailsCarrier(nil); stderr.As(err, &c) && c.Details() != nil {
		de.DetailsField = c.Details()
	}
	if c := StatusCarrier(nil); stderr.As(err, &c) && c.Status() != "" {
		de.StatusField = c.Status()
	}
	if c := StatusCodeCarrier(nil); stderr.As(err, &c) && c.StatusCode() != 0 {
		de.CodeField = c.StatusCode()
	}
	if c := DebugCarrier(nil); stderr.As(err, &c) {
		de.DebugField = c.Debug()
	}
	if c := IDCarrier(nil); stderr.As(err, &c) {
		de.IDField = c.ID()
	}

	if de.StatusField == "" {
		de.StatusField = http.StatusText(de.StatusCode())
	}

	return de
}

I think the problem may come from here.

BalteMD avatar Mar 19 '25 12:03 BalteMD

I'm not sure how that's possible, the code is pretty clear on which status code is being sent: https://github.com/ory/herodot/blob/07b33b42390e7534213a1f72e760f8f367677017/json.go#L162

aeneasr avatar Mar 20 '25 09:03 aeneasr

@aeneasr When initializing h := NewJSONWriter(nil), it will create a default object ErrorContainer{Error: ToDefaultError(err, r.Header.Get("X-Request-ID"))}.

func NewJSONWriter(reporter ErrorReporter) *JSONWriter {
	writer := &JSONWriter{
		Reporter:      reporter,
		ErrorEnhancer: defaultJSONErrorEnhancer,
	}
	if writer.Reporter == nil {
		writer.Reporter = &stdReporter{}
	}

	writer.ErrorEnhancer = defaultJSONErrorEnhancer
	return writer
}

BalteMD avatar Mar 21 '25 03:03 BalteMD