we-promise/sure · error · ImportSession::ConflictError

import_session_conflict

import_session_conflict

Error message

client_session_id already exists with a different expected_chunks value

What it means

ImportSession.create_or_find_for! (app/models/import_session.rb:103-119) turns a create/reconnect request into HTTP 409 {error: 'import_session_conflict'} when the family already has a persisted session with this client_session_id, both the stored and the newly sent expected_chunks are present, and they differ. client_session_id is the idempotency key of a chunked SureImport upload, and expected_chunks is frozen on first use so the server can later validate that sequences 1..N are all present before publishing.

Source

Thrown at app/models/import_session.rb:118

    end
  end

  def self.create_or_find_for!(family:, import_type:, client_session_id:, expected_chunks:)
    import_type = import_type.presence || "SureImport"
    expected_chunks = normalize_positive_integer(expected_chunks)
    unless IMPORT_TYPES.include?(import_type)
      session = new(import_type: import_type)
      session.errors.add(:import_type, "must be SureImport")
      raise ActiveRecord::RecordInvalid.new(session)
    end

    if client_session_id.present?
      session = family.import_sessions.find_or_initialize_by(client_session_id: client_session_id)
      if session.persisted? &&
         expected_chunks.present? &&
         session.expected_chunks.present? &&
         session.expected_chunks != expected_chunks
        raise ConflictError, "client_session_id already exists with a different expected_chunks value"
      end
    else
      session = family.import_sessions.build
    end

    session.import_type = import_type
    session.expected_chunks ||= expected_chunks
    session.save!
    session
  rescue ActiveRecord::RecordNotUnique
    raise unless client_session_id.present?

    existing = family.import_sessions.find_by(client_session_id: client_session_id)
    raise unless existing

    if expected_chunks.present? &&
       existing.expected_chunks.present? &&
       existing.expected_chunks != expected_chunks

View on GitHub (pinned to e69894adb9)

Solutions

  1. Reuse the original expected_chunks value stored alongside client_session_id on the client
  2. Omit expected_chunks on reconnect — the session keeps its stored value and the conflict check is skipped
  3. If the chunk plan genuinely changed, start a new client_session_id (chunks from the old session do not carry over)
  4. GET the existing session and read its expected_chunks before re-sending the create

Example fix

# before
POST /api/v1/import_sessions
{ "type": "SureImport", "client_session_id": "job-42", "expected_chunks": 3 }
# → 409: session job-42 was created with expected_chunks=5

# after
{ "type": "SureImport", "client_session_id": "job-42", "expected_chunks": 5 }
Defensive patterns

Strategy: validation

Validate before calling

plan = load_upload_plan # persisted {client_session_id:, expected_chunks:} from the first attempt
payload = {
  type: 'SureImport',
  client_session_id: plan[:client_session_id],
  expected_chunks: plan[:expected_chunks] # same value as the original create
}
api.post('/api/v1/import_sessions', payload)

Prevention

When it happens

Trigger: POST /api/v1/import_sessions with the same client_session_id twice — first with expected_chunks=5, then expected_chunks=3 — e.g. the client re-split the file into fewer chunks and reused the session id.

Common situations: Client re-splits the NDJSON after a partial failure and sends a new chunk count under the old id; stale local state after a reinstall; two devices resuming the same session with different chunk plans.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/fb1378fccd546788. Report an issue: GitHub.