ankane/searchkick · error · Searchkick::MissingIndexError

No search context found for id

Error message

No search context found for id

What it means

When a scrolled search returns 404 with 'No search context found for id', the scroll cursor no longer exists on the server — the keep-alive (searchkick default `1m`) expired between batches, the scroll was explicitly cleared, or the node restarted/failed over. `Query#handle_error` maps this to `Searchkick::MissingIndexError` with the message 'No search context found for id'.

Source

Thrown at lib/searchkick/query.rb:193

        puts "Results"
        puts JSON.pretty_generate(response.to_h)
      end

      # set execute for multi search
      @execute = Results.new(searchkick_klass, response, opts)
    end

    def retry_misspellings?(response)
      @misspellings_below && response["error"].nil? && Results.new(searchkick_klass, response).total_count < @misspellings_below
    end

    private

    def handle_error(e)
      status_code = e.message[1..3].to_i
      if status_code == 404
        if e.message.include?("No search context found for id")
          raise MissingIndexError, "No search context found for id"
        else
          raise MissingIndexError, "Index missing - run #{reindex_command}"
        end
      elsif status_code == 500 && (
        e.message.include?("IllegalArgumentException[minimumSimilarity >= 1]") ||
        e.message.include?("No query registered for [multi_match]") ||
        e.message.include?("[match] query does not support [cutoff_frequency]") ||
        e.message.include?("No query registered for [function_score]")
      )

        raise UnsupportedVersionError
      elsif status_code == 400
        if (
          e.message.include?("bool query does not support [filter]") ||
          e.message.include?("[bool] filter does not support [filter]")
        )

          raise UnsupportedVersionError

View on GitHub (pinned to 93e901a75b)

Solutions

  1. Increase the keep-alive to comfortably exceed your worst-case per-batch time: `scroll: "10m"` (each batch request extends it).
  2. Move expensive per-batch work out of the scroll loop — collect ids in the loop, process afterwards.
  3. Rescue `Searchkick::MissingIndexError` and restart the scroll from the beginning (make the job idempotent, e.g. keyed on updated_at or a cursor table).
  4. If eviction is the cause, raise `search.max_open_scroll_context` on the cluster or ensure scroll cursors are closed promptly.

Example fix

# before
Product.search("*", scroll: "1m", batch_size: 1000) do |batch|
  HeavyExport.run(batch) # slower than 1m => MissingIndexError
end

# after
Product.search("*", scroll: "10m", batch_size: 1000) do |batch|
  ids += batch.map(&:id) # cheap loop; heavy work afterwards
end
HeavyExport.run_later(ids)
Defensive patterns

Strategy: retry

Validate before calling

# Before long jobs, size scroll keep-alive to worst-case batch time
per_batch_seconds = 45
scroll_keepalive = "#{(per_batch_seconds * 4 / 60.0).ceil}m" # 4x headroom
Product.search("*", scroll: scroll_keepalive, batch_size: 500) { |batch| process(batch) }

Try / catch

def with_scroll_retry(klass, attempts: 2)
  yield
rescue Searchkick::MissingIndexError => e
  raise unless e.message.include?("No search context found")
  retry if (attempts -= 1) > 0 # restart scroll from beginning; job must be idempotent
  raise
end

with_scroll_retry(Product) do
  Product.search("*", scroll: "10m", batch_size: 1000) { |b| Export.run(b) }
end

Prevention

When it happens

Trigger: `Model.search("*", scroll: "1m", batch_size: 1000) { |batch| ... }` where per-batch processing (e.g. slow API calls, large N+1 loading) exceeds the scroll keep-alive; resuming/serializing a scroll cursor after a long pause; scrolling during a node restart or when the search context is evicted (max_open_scroll_context limit hit).

Common situations: Bulk export/reindex jobs that do heavy per-batch work; parallel scroll workers sharing cursors; low `search.max_open_scroll_context` defaults causing eviction; ES node maintenance mid-scroll.

Related errors


AI-assisted analysis of ankane/searchkick@93e901a75b (2026-08-21). Data as JSON: /api/errors/eb8b56e79a95a04a. Report an issue: GitHub.