{"record":{"id":"b76693446829d702","repo":"abhigyanpatwari/GitNexus","slug":"parameter-grouponlyarg-is-not-available-in-gi","errorCode":null,"errorMessage":"Parameter \"${groupOnlyArg}\" is not available in GitNexus MCP read-only mode.","messagePattern":"Parameter \"(.+?)\" is not available in GitNexus MCP read-only mode\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/mcp/read-only-policy.ts","lineNumber":47,"sourceCode":"\nexport function assertMcpReadOnlyToolCall(\n  toolName: string,\n  args: Record<string, unknown> | undefined,\n  readOnly: boolean,\n): void {\n  if (!readOnly) return;\n  if (!MCP_READ_ONLY_TOOLS.has(toolName) && !MCP_READ_ONLY_ALIASES.has(toolName)) {\n    throw new Error(`Tool \"${toolName}\" is not available in GitNexus MCP read-only mode.`);\n  }\n  if (typeof args?.repo === 'string' && args.repo.trim().startsWith('@')) {\n    throw new Error('Group routing is not available in GitNexus MCP read-only mode.');\n  }\n  // crossDepth/subgroup only do anything on the @group path rejected above,\n  // but rejecting them here keeps the advertised schema and the dispatch\n  // contract in agreement.\n  for (const groupOnlyArg of ['crossDepth', 'subgroup']) {\n    if (args?.[groupOnlyArg] !== undefined) {\n      throw new Error(\n        `Parameter \"${groupOnlyArg}\" is not available in GitNexus MCP read-only mode.`,\n      );\n    }\n  }\n}\n\nexport function readOnlyResourceTemplateAllowed(uriTemplate: string, readOnly: boolean): boolean {\n  return !readOnly || !/^gitnexus:\\/\\/group\\//iu.test(uriTemplate);\n}\n\nexport function assertMcpReadOnlyResource(uri: string, readOnly: boolean): void {\n  if (!readOnly) return;\n\n  let isGroupResource = false;\n  try {\n    const parsed = new URL(uri);\n    isGroupResource =\n      parsed.protocol.toLowerCase() === 'gitnexus:' && parsed.hostname.toLowerCase() === 'group';","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/mcp/read-only-policy.ts#L29-L65","documentation":"Thrown by assertMcpReadOnlyToolCall in read-only mode when a tool call includes crossDepth or subgroup — parameters that only have meaning on the (disabled) @group routing path. The code comments explain they are rejected anyway so the advertised schema and the dispatch contract stay in agreement: read-only mode must not appear to accept group-only knobs.","triggerScenarios":"With GITNEXUS_MCP_READ_ONLY=1, calling a tool with arguments containing crossDepth or subgroup, even with a normal non-group repo — e.g. { repo: 'frontend', subgroup: 'services', crossDepth: 2 }.","commonSituations":"A client built for cross-repo group queries sends both parameters unconditionally. An agent template with default arguments (subgroup: undefined is fine, but explicit null or a value triggers it — the check is args[key] !== undefined, so pass undefined or omit, never null).","solutions":["Remove crossDepth and subgroup from the arguments object entirely before sending in read-only mode.","Strip null values too — the guard tests !== undefined, so subgroup: null also throws; omit the key or set it to undefined.","If group parameters are needed, run against a server without read-only mode enabled."],"exampleFix":"// before\nconst args = { search_query: 'auth', repo: 'frontend', subgroup: 'services', crossDepth: 2 };\nawait client.callTool({ name: 'query', arguments: args });\n\n// after\nconst args = { search_query: 'auth', repo: 'frontend' };\nawait client.callTool({ name: 'query', arguments: args });","handlingStrategy":"validation","validationCode":"const GROUP_ONLY_ARGS = ['crossDepth', 'subgroup'];\nfunction stripGroupOnlyArgs(args: Record<string, unknown>, readOnly: boolean) {\n  if (!readOnly) return args;\n  const clean = { ...args };\n  for (const k of GROUP_ONLY_ARGS) {\n    if (clean[k] !== undefined) delete clean[k]; // null triggers too: !== undefined check\n  }\n  return clean;\n}","typeGuard":"const hasNoGroupOnlyArgs = (args: Record<string, unknown>): boolean =>\n  args.crossDepth === undefined && args.subgroup === undefined;","tryCatchPattern":"try {\n  await client.callTool({ name: 'query', arguments });\n} catch (e) {\n  if (e instanceof Error && /Parameter \"(crossDepth|subgroup)\" is not available/.test(e.message)) {\n    const { crossDepth, subgroup, ...rest } = arguments;\n    return client.callTool({ name: 'query', arguments: rest }); // one retry with cleaned args\n  }\n  throw e;\n}","preventionTips":["Remember null is not omitted: the server checks !== undefined, so never default these params to null.","Keep group-only parameters in a separate object you merge only when group routing is actually used.","Add a client-side schema per deployment mode (readonly vs full) and validate arguments before dispatch."],"tags":["mcp","read-only-mode","argument-validation","group-routing"],"backgroundTag":"readonly-mode-restriction","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-08T20:17:18.057Z"}