gofr-dev/gofr · error

internal server error

Error message

internal server error

What it means

errEmptyResponse is the sentinel error in pkg/gofr/http/responder.go that gofr's HTTP responder returns when a handler yields a nil/empty response that cannot be serialized into a valid HTTP reply. Rather than sending an empty body with a 200, the framework surfaces this as an 'internal server error' to the client, since it indicates the handler failed to produce any response payload.

Source

Thrown at pkg/gofr/http/responder.go:15

package http

import (
	"bytes"
	"encoding/json"
	"errors"
	"net/http"
	"reflect"
	"sync"

	resTypes "gofr.dev/pkg/gofr/http/response"
)

var (
	errEmptyResponse = errors.New("internal server error")
)

// maxRespPooledBuf caps the capacity of a response buffer returned to the pool
// so an occasional very large response does not permanently inflate every
// pooled buffer.
const maxRespPooledBuf = 64 << 10

// initialRespBufCap is the starting capacity of a freshly minted pooled buffer,
// sized to hold a typical small JSON response without a reslice.
const initialRespBufCap = 512

// respBufPool holds reusable buffers for encoding JSON response bodies. Encoding
// into a pooled buffer avoids the fresh []byte json.Marshal returns on every
// response and collapses the body + trailing newline into a single Write. A
// process-wide pool is the idiomatic shape for this and is safe for concurrent
// use by construction.
//
//nolint:gochecknoglobals // process-wide pool of reusable response-encode buffers.

View on GitHub (pinned to 187eb24962)

Solutions

  1. Inspect the handler returning the empty response and ensure every code path returns a non-nil response object or a real error
  2. Log the handler name and inputs to find which route produced the empty payload
  3. Add unit tests (like Test_getPublicKeys) covering all return paths of the handler
  4. If nil is legitimate, return an explicit empty-but-non-nil response struct instead

Example fix

// before
func handler(ctx *gofr.Context) (any, error) {
    if ctx.Param("id") == "" {
        return nil, nil // yields internal server error
    }
    return data, nil
}
// after
func handler(ctx *gofr.Context) (any, error) {
    if ctx.Param("id") == "" {
        return emptyResponse{}, nil
    }
    return data, nil
}
Defensive patterns

Strategy: validation

Validate before calling

resp, err := handler(ctx)
if err != nil {
    return err
}
if resp == nil {
    return errors.New("handler returned nil response")
}

Type guard

func isNilResponse(v any) bool {
    if v == nil { return true }
    rv := reflect.ValueOf(v)
    switch rv.Kind() {
    case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface:
        return rv.IsNil()
    }
    return false
}

Try / catch

if resp, err := doRequest(client, url); err != nil {
    if errors.Is(err, errEmptyResponse) {
        log.Printf("handler produced no response: %v", err)
        http.Error(w, "no content produced", http.StatusUnprocessableEntity)
        return
    }
    return err
}

Prevention

When it happens

Trigger: A handler registered via gofr returns nil (or a zero-value interface) as its response object, so getPublicKeys/determineResponse in the responder pipeline find nothing to encode and errEmptyResponse is raised.

Common situations: Handler logic that forgets its return statement value or returns nil on an early-exit path; refactoring a handler to a typed response but leaving a nil-returning branch; middleware swallowing the response object.

Understand the failure class

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/1ce10042325b8dc2. Report an issue: GitHub.