owasp-amass/amass · error

%s/stats: status=%s error=%s

Error message

%s/stats: status=%s error=%s

What it means

Returned by Client.SessionStats when GET /api/v1/sessions/{token}/stats returns a non-200 status and the response body is a decodable JSON error, so the message includes the server's error text after 'error=': '<token>/stats: status=<status> error=<msg>'. It reports exactly why the server refused to return statistics for that session.

Source

Thrown at engine/api/client/v1/client.go:180

		return fmt.Errorf("terminateSession: status=%s error=%s", resp.Status, msg)
	}
	return nil
}

// Retrieves statistics for the session associated with the provided token.
func (c *Client) SessionStats(ctx context.Context, token uuid.UUID) (*et.SessionStats, error) {
	resp, err := amasshttp.RequestWebPage(ctx, c.httpClient,
		&amasshttp.Request{URL: c.base + "/sessions/" + token.String() + "/stats"})
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		msg, err := readJSONError(resp.Body)
		if err != nil {
			return nil, fmt.Errorf("%s/stats: status=%s", token.String(), resp.Status)
		}
		return nil, fmt.Errorf("%s/stats: status=%s error=%s", token.String(), resp.Status, msg)
	}

	var st et.SessionStats
	if err := json.Unmarshal([]byte(resp.Body), &st); err != nil {
		return nil, err
	}
	return &st, nil
}

// Retrieves scope for the session associated with the provided token.
func (c *Client) SessionScope(ctx context.Context, token uuid.UUID, atype oam.AssetType) ([]oam.Asset, error) {
	sessionID := token.String()
	atypestr := strings.ToLower(string(atype))
	u := fmt.Sprintf("%s/sessions/%s/scope/%s", c.base, sessionID, atypestr)
	resp, err := amasshttp.RequestWebPage(ctx, c.httpClient, &amasshttp.Request{URL: u})
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 79299dce87)

Solutions

  1. Read the 'error=' text - it is the server's specific reason; for 'not found' re-create the session or pick a live token from ListSessions
  2. For 401/403, fix the server's auth configuration or the client's credentials
  3. For 5xx errors, retry with backoff and check server logs
  4. Confirm the token belongs to the server instance you are querying
  5. Add a HealthCheck gate before stats polling loops so a down server is detected early

Example fix

// before: polling stats without handling dead sessions
for {
    st, err := client.SessionStats(ctx, token)
    if err != nil {
        log.Print(err) // loops forever on 404
    }
}

// after: recreate the session when the server says it is gone
st, err := client.SessionStats(ctx, token)
if err != nil && strings.Contains(err.Error(), "404") {
    token, err = client.CreateSession(ctx, cfg)
    if err == nil {
        st, err = client.SessionStats(ctx, token)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: validate token is live before requesting stats
tokens, err := c.ListSessions(ctx)
if err != nil {
    return err
}
live := false
for _, t := range tokens {
    if t == token {
        live = true
        break
    }
}
if !live {
    return fmt.Errorf("session %s is not active; recreate before querying stats", token)
}

Try / catch

// Go
st, err := c.SessionStats(ctx, token)
if err != nil {
    msg := err.Error()
    switch {
    case strings.Contains(msg, "404"):
        // server told us the session is gone - recreate
        token, err = c.CreateSession(ctx, cfg)
        if err == nil {
            st, err = c.SessionStats(ctx, token)
        }
    case strings.Contains(msg, "401"), strings.Contains(msg, "403"):
        err = fmt.Errorf("auth rejected stats request: %w", err)
    default:
        err = backoffRetryStats(ctx, c, token)
    }
}

Prevention

When it happens

Trigger: Calling SessionStats(ctx, token) (or getStats) when the server rejects the request with a JSON error body: unknown or expired session token (404 with JSON message), auth rejection (401/403), or internal server error while computing stats (500).

Common situations: Session expired between TerminateSession/CreateSession cycles; token from a different engine instance; server-side auth misconfiguration; transient 500 while the server aggregates stats.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of owasp-amass/amass@79299dce87 (2026-09-06). Data as JSON: /api/errors/55a969c433a60f37. Report an issue: GitHub.