{"record":{"id":"8546a397d4267c39","repo":"m1k1o/neko","slug":"error-response-from-backend","errorCode":null,"errorMessage":"error response from backend","messagePattern":"error response from backend","errorType":"error_code","errorClass":"ErrBackendRespone","httpStatus":null,"severity":"error","filePath":"server/internal/http/legacy/session.go","lineNumber":24,"sourceCode":"\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\n\toldTypes \"github.com/m1k1o/neko/server/internal/http/legacy/types\"\n\n\t\"github.com/m1k1o/neko/server/internal/api\"\n\t\"github.com/m1k1o/neko/server/pkg/types\"\n\n\t\"github.com/gorilla/websocket\"\n\t\"github.com/rs/zerolog\"\n)\n\nvar (\n\tErrWebsocketSend  = fmt.Errorf(\"failed to send message to websocket\")\n\tErrBackendRespone = fmt.Errorf(\"error response from backend\")\n)\n\ntype memberStruct struct {\n\tmember    *oldTypes.Member\n\tconnected bool\n\tsent      bool\n}\n\ntype session struct {\n\tr *http.Request\n\th *LegacyHandler\n\n\tlogger     zerolog.Logger\n\tserverAddr string\n\tpathPrefix string\n\n\tid, ip  string\n\ttoken   string","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/http/legacy/session.go#L6-L42","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the wrapped message (fmt.Errorf %s) and the backend logs to find the actual status code and cause.","Use errors.Is(err, ErrBackendRespone) to branch on backend rejection vs other failures.","Verify backend URL, credentials/token, and API compatibility with the running backend version.","Add retry with backoff for transient 5xx responses in apiReq callers."],"exampleFix":"// before\nif err := s.apiReq(http.MethodGet, \"/api/sessions\", nil, &sessions); err != nil {\n    return err\n}\n// after\nif err := s.apiReq(http.MethodGet, \"/api/sessions\", nil, &sessions); err != nil {\n    if errors.Is(err, ErrBackendRespone) {\n        return fmt.Errorf(\"backend rejected sessions fetch: %w\", err)\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"resp, err := http.Head(backendURL + \"/api/stats\")\nif err != nil || resp.StatusCode >= 400 {\n    return fmt.Errorf(\"backend not healthy before calling apiReq: status %v\", resp.StatusCode)\n}","typeGuard":"func isBackendResponseError(err error) bool {\n    return errors.Is(err, ErrBackendRespone)\n}","tryCatchPattern":"if err := s.apiReq(method, path, req, &out); err != nil {\n    if errors.Is(err, ErrBackendRespone) {\n        // backend rejected: log wrapped message, maybe re-auth\n        return fmt.Errorf(\"backend error: %w\", err)\n    }\n    return err\n}","preventionTips":["Always match with errors.Is, never string comparison on the message.","Keep the backend URL/auth configuration validated at startup.","Monitor backend health and alert on rising non-2xx rates.","Pin compatible proxy/backend versions to avoid API drift."],"tags":["http","backend","sentinel-error"],"backgroundTag":"backend-error-response","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}