t8y2/dbx · error · java.lang.IllegalArgumentException

Index options cannot contain "key"; specify index fields in

Error message

Index options cannot contain "key"; specify index fields in keys JSON

What it means

In createIndex, index fields must be given via keys_json; the options_json document is merged into the index spec, so an option named "key" would collide with the required "key" field and corrupt the specification. The agent therefore rejects options containing a "key" entry with IllegalArgumentException. This is a schema conflict guard, not a MongoDB server rule.

Source

Thrown at agents/drivers/mongodb/src/main/java/com/dbx/agent/mongodb/MongoAgent.java:1010

            // Without an explicit old version, preserve MongoDB's single-command array semantics.
            return false;
        }
    }

    private static Object createIndex(JsonObject params) {
        MongoClient c = requireClient();
        String database = params.get("database").getAsString();
        String collection = params.get("collection").getAsString();
        Document keys = requiredDocument(params, "keys_json", "Index keys");
        if (keys.isEmpty()) {
            throw new IllegalArgumentException("Index keys are required");
        }

        Document index = new Document("key", keys);
        Document options = documentOrNull(params, "options_json");
        if (options != null) {
            if (options.containsKey("key")) {
                throw new IllegalArgumentException("Index options cannot contain \"key\"; specify index fields in keys JSON");
            }
            index.putAll(options);
        }
        String name;
        if (!index.containsKey("name")) {
            name = defaultIndexName(keys);
            index.put("name", name);
        } else if (!(index.get("name") instanceof String)) {
            throw new IllegalArgumentException("Index option \"name\" must be a non-empty string");
        } else {
            name = (String) index.get("name");
            if (name.isBlank()) {
                throw new IllegalArgumentException("Index option \"name\" must be a non-empty string");
            }
        }

        c.getDatabase(database).runCommand(
            new Document("createIndexes", collection)

View on GitHub (pinned to c0390bff16)

Solutions

  1. Move the key fields out of options_json into keys_json, keeping only true options (unique, name, expireAfterSeconds, etc.) in options_json.
  2. If you have a full index spec, split it: "key" goes to keys_json, everything else to options_json.
  3. Validate the options map before sending to ensure it has no "key" entry.

Example fix

// before
{"keys_json": {}, "options_json": {"key": {"email": 1}, "unique": true}}
// after
{"keys_json": {"email": 1}, "options_json": {"unique": true}}
Defensive patterns

Strategy: validation

Validate before calling

if (params.options_json && "key" in params.options_json) {
  // move it: params.keys_json = params.options_json.key; delete params.options_json.key;
  throw new Error("Move index fields from options_json.key into keys_json");
}

Type guard

function optionsHaveNoKey(options) {
  return options == null || !("key" in options);
}

Try / catch

try {
  await agent.createIndex({ database, collection, keys_json: keys, options_json: options });
} catch (e) {
  if (String(e.message).includes('cannot contain "key"')) {
    const { key, ...rest } = options; // split and retry
    await agent.createIndex({ database, collection, keys_json: key, options_json: rest });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling createIndex with options_json that includes a "key" field, e.g. {"options_json": {"key": {"field": 1}, "unique": true}}.

Common situations: Confusing keys and options — putting index fields into options_json instead of keys_json; merging a full index spec (as returned by getIndexes) into options; copy-paste from server-side index documents.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/500062dd816b8147. Report an issue: GitHub.