m1k1o/neko · error

unexpected status code: %d, body: %s

Error message

unexpected status code: %d, body: %s

What it means

This error is the fallback branch of session.req: the backend returned a non-success status code and the body could NOT be parsed as a JSON object with a 'message' field, so the raw status code and (trimmed) body are embedded in the error. It indicates an unexpected backend response format — often an HTML error page, empty body, or plain text from a proxy/load balancer rather than the application backend itself.

Source

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

	res, err := s.client.Do(req)
	if err != nil {
		return nil, nil, err
	}

	if res.StatusCode < 200 || res.StatusCode >= 300 {
		defer res.Body.Close()

		body, _ := io.ReadAll(res.Body)
		// try to unmarsal as json error message
		var apiErr struct {
			Message string `json:"message"`
		}
		if err := json.Unmarshal(body, &apiErr); err == nil {
			return nil, nil, fmt.Errorf("%w: %s", ErrBackendRespone, apiErr.Message)
		}
		// return raw body if failed to unmarshal
		return nil, nil, fmt.Errorf("unexpected status code: %d, body: %s", res.StatusCode, strings.TrimSpace(string(body)))
	}

	return res.Body, res.Header, nil
}

func (s *session) apiReq(method, path string, request, response any) error {
	reqBody, err := json.Marshal(request)
	if err != nil {
		return err
	}

	headers := http.Header{
		"Content-Type": []string{"application/json"},
	}

	resBody, _, err := s.req(method, path, headers, bytes.NewReader(reqBody))
	if err != nil {
		return err

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Check the status code and body text embedded in the error to see which infrastructure layer answered.
  2. Verify the backend process is running and reachable at the configured URL (curl the endpoint directly).
  3. Inspect reverse proxy / load balancer logs between the proxy and backend.
  4. Fix infrastructure issues or make the backend return structured JSON errors on failure paths.

Example fix

// before
if err := s.apiReq("GET", "/api/stats", nil, &newStats); err != nil {
    return err
}
// after
if err := s.apiReq("GET", "/api/stats", nil, &newStats); err != nil {
    if strings.Contains(err.Error(), "unexpected status code: 50") {
        return fmt.Errorf("backend unavailable, retry later: %w", err)
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get(backendURL + "/api/stats")
if err != nil {
    return fmt.Errorf("backend unreachable: %w", err)
}
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
    return fmt.Errorf("backend answered non-JSON (%s); infrastructure issue likely", ct)
}

Type guard

func isNonJSONStatusError(err error) bool {
    return strings.Contains(err.Error(), "unexpected status code:")
}

Try / catch

err := s.apiReq(method, path, req, &out)
if strings.Contains(err.Error(), "unexpected status code: 50") {
    // transient infra failure: retry with backoff
    time.Sleep(2 * time.Second)
    err = s.apiReq(method, path, req, &out)
}

Prevention

When it happens

Trigger: apiReq hits a non-2xx response whose body is not JSON — e.g. an nginx 502 Bad Gateway HTML page, an empty 500 body, a Cloudflare block page, or a plain-text panic output.

Common situations: Backend process down behind a reverse proxy (502/504); TLS or routing misconfiguration landing on the wrong service; WAF/CDN intercepting requests; backend crash producing non-JSON output.

Related errors


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