-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Minor - P4
-
Affects Version/s: 1.17.9, 2.8.0
-
Component/s: None
-
None
-
None
-
Fully Compatible
-
Go Drivers
-
Needed
-
-
None
-
None
-
None
-
None
-
None
-
None
-
-
- 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`.
- 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.)
- 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):
- Summary
-
- `i=0, we.Index=1` → `idIndex = 1` → OK
- `i=1, we.Index=0` → `idIndex = 0-1 = -1` → `result[:-1]` → *panic*
-
- 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.)
- 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.
- 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).
- Environment
- Actual panic observed
-
- 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