m1k1o/neko · error · ErrBackendRespone

error response from backend

Error message

error response from backend

What it means

ErrBackendRespone (sic) is the sentinel error signaling that the legacy proxy's backend HTTP request returned a non-success status code. It is returned by session.req (via apiReq) and wrapped with the backend's JSON message or a raw body, so callers should use errors.Is to detect backend rejection regardless of formatting. The misspelling is part of the public API and must be matched exactly.

Source

Thrown at server/internal/http/legacy/session.go:24

	"fmt"
	"io"
	"net/http"
	"path"
	"strings"
	"sync"

	oldTypes "github.com/m1k1o/neko/server/internal/http/legacy/types"

	"github.com/m1k1o/neko/server/internal/api"
	"github.com/m1k1o/neko/server/pkg/types"

	"github.com/gorilla/websocket"
	"github.com/rs/zerolog"
)

var (
	ErrWebsocketSend  = fmt.Errorf("failed to send message to websocket")
	ErrBackendRespone = fmt.Errorf("error response from backend")
)

type memberStruct struct {
	member    *oldTypes.Member
	connected bool
	sent      bool
}

type session struct {
	r *http.Request
	h *LegacyHandler

	logger     zerolog.Logger
	serverAddr string
	pathPrefix string

	id, ip  string
	token   string

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Inspect the wrapped message (fmt.Errorf %s) and the backend logs to find the actual status code and cause.
  2. Use errors.Is(err, ErrBackendRespone) to branch on backend rejection vs other failures.
  3. Verify backend URL, credentials/token, and API compatibility with the running backend version.
  4. Add retry with backoff for transient 5xx responses in apiReq callers.

Example fix

// before
if err := s.apiReq(http.MethodGet, "/api/sessions", nil, &sessions); err != nil {
    return err
}
// after
if err := s.apiReq(http.MethodGet, "/api/sessions", nil, &sessions); err != nil {
    if errors.Is(err, ErrBackendRespone) {
        return fmt.Errorf("backend rejected sessions fetch: %w", err)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

resp, err := http.Head(backendURL + "/api/stats")
if err != nil || resp.StatusCode >= 400 {
    return fmt.Errorf("backend not healthy before calling apiReq: status %v", resp.StatusCode)
}

Type guard

func isBackendResponseError(err error) bool {
    return errors.Is(err, ErrBackendRespone)
}

Try / catch

if err := s.apiReq(method, path, req, &out); err != nil {
    if errors.Is(err, ErrBackendRespone) {
        // backend rejected: log wrapped message, maybe re-auth
        return fmt.Errorf("backend error: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Any session.apiReq call (e.g. GET /api/sessions, /api/stats, /api/room/settings from Route, or create/destroy) where the backend responds with a non-2xx status and the body parses as JSON with a 'message' field.

Common situations: Backend restarted or upgrading (502/503); wrong backend URL/port configured; auth token expired causing 401/403; rate limiting on the backend; version mismatch between proxy and backend API routes.

Related errors


AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01). Data as JSON: /api/errors/8546a397d4267c39. Report an issue: GitHub.