Collection.InsertMany can panic on multiple write errors

XMLWordPrintableJSON

    • Type: Bug
    • Resolution: Fixed
    • Priority: Minor - P4
    • 2.8.3
    • Affects Version/s: 1.17.9, 2.8.0
    • Component/s: None
    • None
    • None
    • Fully Compatible
    • Go Drivers
    • Needed
    • Hide

      1. What would you like to communicate to the user about this feature?
      2. Would you like the user to see examples of the syntax and/or executable code and its output?
      3. Which versions of the driver/connector does this apply to?

      Show
      1. What would you like to communicate to the user about this feature? 2. Would you like the user to see examples of the syntax and/or executable code and its output? 3. Which versions of the driver/connector does this apply to?
    • None
    • None
    • None
    • None
    • None
    • None

          1. Summary
             
            `Collection.InsertMany` (via `Collection.insert`) can panic with `runtime error: slice bounds out of range [:-1]` when using unordered inserts (`SetOrdered(false)`) if 2 or more documents fail and the server's returned `WriteErrors` are not sorted in ascending order by `Index`.
             
          2. Where
             
            `mongo/collection.go`, inside `Collection.insert`'s post-processing of a `driver.WriteCommandError`:
             
            ```go
            for i, we := range wce.WriteErrors {
                // i indexes have been removed before the current error, so the index is we.Index-i
                idIndex := int(we.Index) - i
                // if the insert is ordered, nothing after the error was inserted
                if imo.Ordered == nil || *imo.Ordered {         result = result[:idIndex]         break     }

                result = append(result[:idIndex], result[idIndex+1:]...)       <--- panic
            }
            ```
             
            (Line numbers as of v1.17.9; the same logic, same shape, exists unchanged on `master` as of writing.)
             

          3. Root cause
             
            The loop assumes `wce.WriteErrors` arrives sorted ascending by `Index`, so that subtracting the running count `i` of already-removed entries always keeps `idIndex >= 0`. For *unordered* inserts this assumption doesn't hold — the driver never sorts `WriteErrors` before this loop runs (confirmed via `x/mongo/driver/operation.go`, which just appends whatever order the server response returned), and the server is not required to report unordered write errors in ascending index order.
             
            Concrete failure: submit `N` documents where 2+ fail. If the server returns, say, `WriteErrors = [\{Index: 1}, \{Index: 0}]` (a legitimate response shape for an unordered write):
      • `i=0, we.Index=1` → `idIndex = 1` → OK
      • `i=1, we.Index=0` → `idIndex = 0-1 = -1` → `result[:-1]` → *panic*
         
          1. Actual panic observed
             
            ```
            panic: runtime error: slice bounds out of range [:-1]
             
            goroutine 545 [running]:
            go.mongodb.org/mongo-driver/mongo.(*Collection).insert(...)
            .../mongo/collection.go:335 +0x1b58
            go.mongodb.org/mongo-driver/mongo.(*Collection).InsertMany(...)
            .../mongo/collection.go:392 +0x7c
            ```
             
            (rest of our application's own stack trimmed — this occurred inside a background batch-insert goroutine using `InsertMany(ctx, batch, options.InsertMany().SetOrdered(false))` against a small (~2-doc) batch where multiple documents happened to fail.)
             
          2. Reproducibility note (please read before triaging)
             
            We were *not* able to build a fully deterministic minimal repro. Forcing 2+ documents to fail in one unordered batch is trivial (duplicate `_id`s), but forcing the server to report those failures out of ascending order is not something we could control from the client side — against our test deployment (single-node, no sharding), write errors for an all-duplicate-key unordered batch were always returned in ascending order across 40+ attempts at varying batch sizes (5 and 50 documents), both through the Go driver and directly via `mongosh`. We only observed the actual panic once, during heavy concurrent load (many simultaneous connections/goroutines against the same deployment), which suggests it may require genuine server-side concurrency/contention to produce out-of-order reporting — not something a simple single-client script reliably forces on demand. The logic flaw itself, however, is independent of reproducibility: the loop's correctness depends on an ordering guarantee that unordered inserts don't provide, which is verifiable by code inspection alone.
             
          3. Suggested fix
             
            Don't rely on `WriteErrors` arriving pre-sorted. E.g., sort a local copy of `wce.WriteErrors` by `Index` ascending before running the removal loop, or use an approach that doesn't depend on iteratively-offset indices at all (e.g., build a `map[int]bool`/set of failed indices up front, then construct the filtered `result` in a single pass over the original slice by position, skipping indices present in the set).
             
          4. Environment
             
      • Driver version: v1.17.9 (also checked: same logic present on `master`)
      • Go version: 1.26
      • Reproducible with: any unordered `InsertMany` where 2+ documents fail (e.g. duplicate `_id`s against a non-time-series collection)

         

        The exact Go version used, with patch level:

      $ go version

      go version go1.26.4 darwin/arm64

       

      The exact version of the Go driver used:

      $ go list -m go.mongodb.org/mongo-driver

      go.mongodb.org/mongo-driver v1.17.9

      Describe how MongoDB is set up. Local vs Hosted, version, topology, load balanced, etc.

      Found on stress testing on localhost (laptop)

      The operating system and version (e.g. Windows 7, OSX 10.8, ...)

      macOS: Tahoo 26.6.1

            Assignee:
            Matt Dale
            Reporter:
            Harm Verhagen (EXT)
            Preston Vasquez
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: