{"record":{"id":"6518a6b822b97b8a","repo":"m1k1o/neko","slug":"w-s","errorCode":null,"errorMessage":"%w: %s","messagePattern":"%w: %s","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/http/legacy/session.go","lineNumber":104,"sourceCode":"\tif s.token != \"\" {\n\t\treq.Header.Set(\"Authorization\", \"Bearer \"+s.token)\n\t}\n\n\tres, err := s.client.Do(req)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif res.StatusCode < 200 || res.StatusCode >= 300 {\n\t\tdefer res.Body.Close()\n\n\t\tbody, _ := io.ReadAll(res.Body)\n\t\t// try to unmarsal as json error message\n\t\tvar apiErr struct {\n\t\t\tMessage string `json:\"message\"`\n\t\t}\n\t\tif err := json.Unmarshal(body, &apiErr); err == nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"%w: %s\", ErrBackendRespone, apiErr.Message)\n\t\t}\n\t\t// return raw body if failed to unmarshal\n\t\treturn nil, nil, fmt.Errorf(\"unexpected status code: %d, body: %s\", res.StatusCode, strings.TrimSpace(string(body)))\n\t}\n\n\treturn res.Body, res.Header, nil\n}\n\nfunc (s *session) apiReq(method, path string, request, response any) error {\n\treqBody, err := json.Marshal(request)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\theaders := http.Header{\n\t\t\"Content-Type\": []string{\"application/json\"},\n\t}\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/http/legacy/session.go#L86-L122","documentation":"This error is produced in session.req when the backend returned a non-success status code AND the response body successfully unmarshaled as JSON containing a 'message' field. It wraps the ErrBackendRespone sentinel with the backend's structured error message, giving callers both a programmatically checkable cause (errors.Is) and the backend's explanation. It means the backend explicitly rejected the request with a JSON error payload.","triggerScenarios":"session.req / apiReq receives e.g. 401 {\"message\":\"unauthorized\"}, 404 {\"message\":\"room not found\"}, or 500 with a JSON body — any non-2xx response whose body is valid JSON with a message field.","commonSituations":"Expired or missing auth token (401); requesting a room/session that no longer exists (404); backend validation failures (400); internal backend errors surfaced as JSON (500).","solutions":["Read the wrapped message (err.Error() contains '%w: <backend message>') to identify the exact backend complaint.","Refresh or fix the auth token if the message indicates unauthorized/forbidden.","Check that the requested resource (room, session id) still exists before calling.","Compare proxy API paths with the backend version's routes; upgrade or downgrade to align API versions."],"exampleFix":"// before\nerr := s.apiReq(\"GET\", \"/api/room/settings\", nil, &settings)\n// after\nerr := s.apiReq(\"GET\", \"/api/room/settings\", nil, &settings)\nif errors.Is(err, legacy.ErrBackendRespone) {\n    log.Error().Err(err).Msg(\"backend rejected request\")\n    // re-authenticate or surface backend message to caller\n}","handlingStrategy":"type-guard","validationCode":"if token == \"\" {\n    return errors.New(\"cannot call backend API: auth token is empty\")\n}","typeGuard":"func backendJSONError(err error) (string, bool) {\n    if errors.Is(err, ErrBackendRespone) {\n        parts := strings.SplitN(err.Error(), \": \", 2)\n        if len(parts) == 2 {\n            return parts[1], true\n        }\n    }\n    return \"\", false\n}","tryCatchPattern":"body, hdr, err := s.req(method, path, req)\nif err != nil {\n    var msg string\n    if errors.Is(err, ErrBackendRespone) {\n        msg = strings.TrimPrefix(err.Error(), \"error response from backend: \")\n        // e.g. re-authenticate on 'unauthorized'\n    }\n    return fmt.Errorf(\"api request failed: %s\", msg)\n}","preventionTips":["Refresh tokens proactively before expiry to avoid 401-driven backend errors.","Validate resource existence (rooms/sessions) before API calls.","Log the wrapped backend message for diagnosis.","Keep error handling keyed on errors.Is(ErrBackendRespone)."],"tags":["http","backend","json","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"}