Skip to content

Grok 4.5: 351 tools fail with HTTP 400 instead of reporting the 350-tool limit #4836

Closed

Description

@kondv

Type: Bug

Copilot CLI Version: 1.0.84-5
OS Version: Windows 11 x64
Model: grok-4.5

Summary

When the total number of advertised tools exceeds 350, requests to grok-4.5 fail with HTTP 400 Bad Request.

Copilot CLI does not enforce or report the tool count limit before making the API call.

Steps to Reproduce

  1. Use a test MCP configuration with no other user/plugin MCP servers enabled, so they do not add to the count.
  2. Save schema-repro.mjs, grok-350.json, and grok-351.json from the Reproducer files section below in the same directory. Node.js must be available.
  3. From that directory, run these native Copilot commands:
copilot --model grok-4.5 --no-custom-instructions --disable-builtin-mcps --disable-mcp-server computer-use --additional-mcp-config "@grok-350.json" --available-tools "mcp:*" --allow-all-tools -p "Reply with exactly OK. Do not call tools."
copilot --model grok-4.5 --no-custom-instructions --disable-builtin-mcps --disable-mcp-server computer-use --additional-mcp-config "@grok-351.json" --available-tools "mcp:*" --allow-all-tools -p "Reply with exactly OK. Do not call tools."

The fixture tools have unique names and identical simple schemas. mcp:* excludes native CLI tools; the MCP-only precondition isolates the fixture's 350/351 count. There is no SDK dependency or shell helper.

Actual Behaviour

With 350 tools:

  • Request succeeds with exit code 0.

With 351 tools:

  • Request immediately fails with:
Execution failed: 400 Bad Request (Request ID: ...)

Comparison:

Tool Count Model Result
350 tools grok-4.5 OK (exit 0)
351 tools grok-4.5 HTTP 400 (exit 1)

The debug tool-surface records confirm exactly 350 and 351 advertised tools for these two runs.

Expected Behaviour

Copilot CLI should track provider-specific limits:

  1. Enforce a tool count budget before sending the request.
  2. If tool count exceeds 350 for Grok, report an actionable error explaining that the active tool count (351) exceeds Grok's maximum limit of 350 tools.
  3. Alternatively, defer tools via tool search rather than sending the entire catalog upfront.

Notes

  • Tested with inert synthetic tools with identical simple schemas ({"type":"object","properties":{"value":{"type":"string"}}}).
  • The issue is purely triggered by the total number of advertised tools, regardless of whether tools are actually invoked by the model.

Impact

Users with multiple MCP servers easily exceed 350 tools in aggregate (native + MCP tools), causing Grok to become completely unusable across the entire session with an unhelpful 400 error.

Reproducer files

schema-repro.mjs

import { createInterface } from 'node:readline';

const mode = process.argv[2];
const count = Number(process.argv[3] ?? 1);
const allowedModes = ['array-enum-bad', 'array-enum-fixed', 'count'];

if (!allowedModes.includes(mode) ||
    !Number.isInteger(count) ||
    count < 1 ||
    count > 400) {
  throw new Error('Usage: node schema-repro.mjs array-enum-bad|array-enum-fixed|count [1..400]');
}

const levels = { type: ['array', 'null'], items: { type: 'integer' } };
if (mode === 'array-enum-bad') {
  levels.enum = [0, 1, 2, 3, 4, 25];
} else {
  levels.items.enum = [0, 1, 2, 3, 4, 25];
}

const tools = Array.from({ length: mode === 'count' ? count : 1 }, (_, index) => ({
  name: `probe_${String(index).padStart(3, '0')}`,
  description: 'Schema diagnostic only; performs no operation.',
  inputSchema: {
    type: 'object',
    properties: mode === 'count' ? { value: { type: 'string' } } : { levels },
  },
}));

for await (const line of createInterface({ input: process.stdin, crlfDelay: Infinity })) {
  if (!line.trim()) {
    continue;
  }
  const request = JSON.parse(line);
  if (request.id === undefined) {
    continue;
  }

  let result;
  if (request.method === 'initialize') {
    result = {
      protocolVersion: request.params.protocolVersion,
      capabilities: { tools: {} },
      serverInfo: { name: 'schema-repro', version: '1.0.0' },
    };
  } else if (request.method === 'tools/list') {
    result = { tools };
  } else if (request.method === 'tools/call') {
    result = {
      isError: true,
      content: [{ type: 'text', text: 'This diagnostic never executes an operation.' }],
    };
  } else if (request.method === 'ping') {
    result = {};
  } else {
    process.stdout.write(`${JSON.stringify({
      jsonrpc: '2.0',
      id: request.id,
      error: { code: -32601, message: 'Method not found' },
    })}\n`);
    continue;
  }

  process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', id: request.id, result })}\n`);
}

grok-350.json

{
  "mcpServers": {
    "schema_repro": {
      "type": "stdio",
      "command": "node",
      "args": ["schema-repro.mjs", "count", "350"],
      "tools": ["*"]
    }
  }
}

grok-351.json

{
  "mcpServers": {
    "schema_repro": {
      "type": "stdio",
      "command": "node",
      "args": ["schema-repro.mjs", "count", "351"],
      "tools": ["*"]
    }
  }
}

Activity

  1. patniko commented on Oct 26, 2026

    @patniko
    Collaborator

    Thanks for the report. We've shipped fixes in this area since this was filed (most recently in Copilot CLI v1.0.91), and we believe this may now be resolved. Could you retest on the latest release (Copilot CLI v1.0.103) and let us know whether you still see it? If we don't hear back in a couple of weeks, we may close this issue — you can always reopen it.

    Triaged as part of a Copilot SDK backlog review, with help from GitHub Copilot.

  2. kondv commented on Oct 28, 2026

    @kondv
    Author

    Thanks @patniko. I retested on 1.0.104 with the same reproducer and it no longer fails. grok-4.5 isn't available anymore, so I ran it on grok-4.6 and grok-4.7, and the 351-tool fixture returns OK on both. The CLI now sends only tool_search_tool up front and defers the MCP tools, so the request doesn't go over the 350 limit. Fine to close from my side.

    Retest on 1.0.104

    Same schema-repro.mjs, grok-350.json and grok-351.json as in the issue. All user and built-in MCP servers were disabled, so only the fixture's tools count:

    copilot --model grok-4.7 --no-custom-instructions --disable-builtin-mcps --disable-mcp-server computer-use --disable-mcp-server <each user MCP server> --additional-mcp-config "@grok-351.json" --available-tools "mcp:*" --allow-all-tools -p "Reply with exactly OK. Do not call tools." --log-level debug --log-dir <dir>
    Fixture tools How enabled Model Result catalog_tool_count Tools sent up front
    350 mcp:* grok-4.7 OK, exit 0 350 1 (tool_search_tool)
    351 mcp:* grok-4.7 OK, exit 0 351 1 (tool_search_tool)
    351 mcp:* grok-4.6 OK, exit 0 351 1 (tool_search_tool)
    351 all 351 names in --available-tools grok-4.7 OK, exit 0 351 1 (tool_search_tool)
    351 all 351 names in --available-tools grok-4.6 OK, exit 0 351 1 (tool_search_tool)

    In the debug log of the 351-tool run, turn tool surface resolved shows "catalog_tool_count":351,"tool_count":1, and the request's copilotToolsFingerprint lists only tool_search_tool.

    — kondv's Copilot 🤖

  3. copilot-cli-bot commented on Oct 28, 2026

    @copilot-cli-bot

    This appears to be resolved as of GitHub Copilot CLI v1.0.105. Please update to that release or a newer stable version. We're closing this as completed. If it still happens, please comment with your version and reproduction steps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions