bytebase/bytebase · error

unexpected status code %d: %s

Error message

unexpected status code %d: %s

What it means

Thrown by getVersion() when the root endpoint GET / returns any status other than 200. Unlike the query path, this requires exactly 200, so even 2xx variants like 204 fail. The full response body is embedded in the message to expose the underlying cluster or proxy error. Wrapped by 2556 in SyncInstance.

Source

Thrown at backend/plugin/db/elasticsearch/sync.go:93

			return "", err
		}
		return info.Version.Number, nil
	}
	resp, err := d.basicAuthClient.Do("GET", []byte("/"), nil)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	bytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", errors.Wrap(err, "failed to read response body")
	}

	// Check HTTP status code
	if resp.StatusCode != http.StatusOK {
		// Include response body for debugging
		return "", errors.Errorf("unexpected status code %d: %s", resp.StatusCode, string(bytes))
	}

	var result VersionResult
	err = json.Unmarshal(bytes, &result)
	if err != nil {
		// Include response body to help debug parsing issues
		bodyPreview, truncated := common.TruncateString(string(bytes), 500)
		if truncated {
			bodyPreview += "..."
		}
		return "", errors.Wrapf(err, "failed to parse version response: %s", bodyPreview)
	}

	return result.Version.Number, nil
}

type IndicesResult struct {
	IndexSize string `json:"store.size"`

View on GitHub (pinned to 1870550677)

Solutions

  1. Read the embedded status and body — it identifies auth (401), permission (403), routing (404), or upstream (502/503) failure.
  2. If 401: correct the username/password (or API key) in the datasource configuration.
  3. If 404: verify the URL and any path prefix; GET / must hit the cluster root exactly.
  4. If 403: grant the sync user access to the root endpoint (cluster 'monitor' privilege) or unblock GET / in the proxy/security rules.
  5. Confirm independently with curl -i http://host:9200/ — it must return HTTP 200 with a JSON version object before Bytebase can sync.

Example fix

// before: root endpoint blocked, GET / returns 403
// xpack: security.rest.roles: deny /
// after: allow authenticated read of the root endpoint
GET / -> 200 {"version":{"number":"8.11.0",...}}
Defensive patterns

Strategy: validation

Validate before calling

// Require exactly 200 from the root endpoint before syncing
resp, err := http.Get("http://es.internal:9200/")
if err != nil || resp.StatusCode != http.StatusOK {
	return fmt.Errorf("GET / must return 200 JSON (got status or network error)")
}

Try / catch

if err != nil {
	if strings.Contains(err.Error(), "unexpected status code") {
		// branch on embedded status: 401 credentials, 403 grants, 404 URL
	}
	return err
}

Prevention

When it happens

Trigger: Instance sync where GET / returns 401 (bad/missing credentials), 403 (root path denied), 404 (wrong URL path/prefix), 502/503 (proxy upstream failure), or any redirect followed to an error page.

Common situations: Missing or wrong datasource credentials; ES behind a proxy that restricts GET /; datasource URL including a wrong base path (e.g. /elasticsearch prefix mismatch); cluster in a failed state answering via LB error pages; security plugin blocking root endpoint for the sync user.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/add6d481d09912e3. Report an issue: GitHub.