Skip to content

Fix search API bugs: timeZone defaulting, epoch time format, aggregate results, and circuit breaker semantics#2

Open
akeyx wants to merge 7 commits into
vinit-devops:mainfrom
akeyx:fix/search-api-bugs
Open

Fix search API bugs: timeZone defaulting, epoch time format, aggregate results, and circuit breaker semantics#2
akeyx wants to merge 7 commits into
vinit-devops:mainfrom
akeyx:fix/search-api-bugs

Conversation

@akeyx

@akeyx akeyx commented Jun 16, 2026

Copy link
Copy Markdown

Summary

This PR upstreams the fixes from @nicolagi's fix/search-api-bugs branch, which make the search tooling (search_logs / execute_query) actually work against the Sumo Logic Search Job API. All commits are authored by @nicolagi; I'm opening the PR (from a fork of their branch) to get the work upstreamed since the commits are unchanged.

Without these fixes, search_logs fails out of the box, so this restores core functionality of the server.

Changes

  • Fix timeZone default not applied when value is None (900096c)
    When search_logs is called without a time_zone, SearchRequest sets it to None, which then reaches the validator and raises APIParameterError (it type-checks None as a string). The validator now skips None params so the existing defaults loop applies UTC.

  • Fix time format: use epoch milliseconds for the search jobs API (5f66f3d)
    /api/v1/search/jobs rejects ISO 8601 strings with "The 'from' field contains an invalid time." to_sumo_api_format / to_sumo_time_format now emit epoch milliseconds, and the API validator accepts the epoch pattern it now produces.

  • Fix result retrieval for aggregate queries and empty results (fab2a72)
    Results were always fetched from /messages, which fails for aggregate queries (count by, sum by, …) with "requireRawMessages is false". get_search_results now tries /records first and falls back to /messages, and handles empty results gracefully instead of raising.

  • Remove unsupported filter_type from list_collectors (afe78fd)
    The Collector API doesn't support filter_type as a query param; removed from the tool schema, tool method, and API client.

  • Stop circuit breaker tripping on benign batches of work (8945b35)
    Reset failure_count on success in the CLOSED state (previously monotonic → "5 ever" rather than "5 consecutive"), and narrow expected_exception to the transient set the retry layer already uses so user errors (e.g. bad-query 4xx) don't count toward opening the breaker.

Test plan

  • search_logs with no time_zone succeeds (defaults to UTC) instead of raising APIParameterError
  • Raw-message search returns results (epoch time format accepted by the jobs API)
  • Aggregate query (... | count by foo) returns records instead of failing with requireRawMessages is false
  • Query matching nothing returns an empty result set rather than an error
  • list_collectors works without the removed filter_type param
  • A session with many small queries does not spuriously open the circuit breaker

Credit: all changes authored by @nicolagi on nicolagi:fix/search-api-bugs.

nicolagi and others added 7 commits February 25, 2026 13:16
When search_logs is called without a time_zone, the SearchRequest model
sets time_zone=None. This None value gets passed into search_params as
timeZone=None. The validator then tries to type-check None as a string,
which raises an APIParameterError.

The fix skips None values during validation so they are treated as
"not provided", allowing the default value (UTC) to be applied in the
subsequent defaults loop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Sumo Logic search jobs API (/api/v1/search/jobs) requires time
values as epoch milliseconds, not ISO 8601 strings. Sending ISO format
results in: "The 'from' field contains an invalid time."

This changes to_sumo_api_format and to_sumo_time_format to output epoch
milliseconds, and adds epoch time pattern validation to the API
validator so it accepts the format it now produces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The search results endpoint always fetched /messages, which fails for
aggregate queries (count by, sum by, etc.) with "requireRawMessages is
false". Aggregate query results are served from /records instead.

This changes get_search_results to try /records first, then fall back
to /messages. Both endpoints are handled gracefully when empty, so
queries that match nothing return an empty result set instead of
raising an error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tool layer passes filter_type to the API client, but the client
method didn't accept it. Add the parameter and filter client-side
since the Sumo Logic API doesn't support it as a query param.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Sumo Logic Collector API does not support filter_type as a query
parameter. Remove it from the tool schema, tool method, and API client
rather than faking it with client-side filtering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two changes to the sumologic-api circuit breaker:

1. resilience.py: reset failure_count to 0 on a success in CLOSED
   state. Previously failure_count was monotonic in CLOSED state, so
   any process accumulating ~5 transient errors over its lifetime
   was guaranteed to trip the breaker eventually — "5 ever" rather
   than "5 consecutive." That's the wrong semantics for an MCP
   server doing dozens of small queries per session.

2. api_client.py: narrow expected_exception from bare Exception to
   the same transient set the retry layer already uses (APIError,
   RateLimitError, TimeoutError, ConnectionError, OSError,
   httpx.RequestError). User errors like 4xx-from-bad-query no
   longer count toward opening the breaker. Updated
   CircuitBreakerConfig.expected_exception's type annotation to
   reflect that a tuple is supported (Python's except clause
   accepts either form at runtime).

Validated with three async test cases:
- failure_count resets on success in CLOSED state
- non-transient exceptions are propagated but don't count
- 3 consecutive transient errors still open the breaker (intended
  behavior preserved)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants