{"record":{"id":"1c48237993ed0005","repo":"abhigyanpatwari/GitNexus","slug":"group-resources-are-unavailable-when-an-mcp-reposi","errorCode":null,"errorMessage":"Group resources are unavailable when an MCP repository allowlist is set.","messagePattern":"Group resources are unavailable when an MCP repository allowlist is set\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gitnexus/src/mcp/repository-policy.ts","lineNumber":305,"sourceCode":"      return backend.selectToolRepository(repo ?? this.defaultRepo?.path, branch, options);\n    }\n    const selected = this.repoForArgs(repo === undefined ? undefined : { repo });\n    // Restricted policies never allow cwd to select outside the configured\n    // set; once policy supplies an explicit path, the public resolver is enough.\n    return backend.resolveRepo(selected?.path, branch);\n  }\n\n  assertResourceUri(uri: string): void {\n    if (!this.restricted) return;\n    let parsed: URL;\n    try {\n      parsed = new URL(uri);\n    } catch {\n      // resources.ts parses with the same URL call, so anything that fails\n      // here fails there too today. Keep obviously group- or repo-shaped\n      // malformed inputs fail-closed anyway in case the parsers ever drift.\n      if (/^gitnexus:\\/\\/group(?:\\/|$)/iu.test(uri)) {\n        throw new Error('Group resources are unavailable when an MCP repository allowlist is set.');\n      }\n      const repoShaped = /^gitnexus:\\/\\/repo\\/([^/]+)/iu.exec(uri);\n      if (repoShaped) this.resolveRuntimeRepo(decodeURIComponent(repoShaped[1]));\n      return;\n    }\n    // gitnexus: is a non-special URL scheme, so the host is opaque and NOT\n    // lowercased by the parser — compare case-insensitively like\n    // read-only-policy.ts does.\n    if (parsed.protocol.toLowerCase() !== 'gitnexus:') return;\n    const hostname = parsed.hostname.toLowerCase();\n    if (hostname === 'group') {\n      throw new Error('Group resources are unavailable when an MCP repository allowlist is set.');\n    }\n    if (hostname !== 'repo') return;\n    const repoName = parsed.pathname.split('/').filter(Boolean)[0];\n    if (!repoName) return;\n    this.resolveRuntimeRepo(decodeURIComponent(repoName));\n  }","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/52924ef12c2290ceee4612526a828ec4cdf2047f/gitnexus/src/mcp/repository-policy.ts#L287-L323","documentation":"Thrown by McpRepositoryPolicy.assertResourceUri in allowlist (restricted) mode when a resource URI fails URL parsing but still matches the group-shaped pattern ^gitnexus:\\/\\/group(\\/|$). It is a fail-closed guard: malformed input that looks like a group resource is rejected here rather than being passed along on the assumption that the downstream parser would reject it anyway.","triggerScenarios":"GITNEXUS_MCP_ALLOWED_REPOS set, then reading a resource whose URI is unparseable by new URL() yet group-shaped — e.g. 'gitnexus:\\/\\/group\\/name\\/contracts' with literal backslashes, or other malformed variants that still begin gitnexus://group.","commonSituations":"URI templating bugs (bad escaping in shell scripts or JSON), hand-built URI strings with wrong slashes, or porting resource URIs through a system that mangles 'gitnexus://group/...' into a non-parsing variant while the allowlist lockdown is active.","solutions":["Fix the URI to a well-formed group form — but note well-formed group URIs are also rejected in restricted mode, so switch to repo-scoped URIs: gitnexus://repo/{name}/context.","Check how the URI string is produced (escaping, encoding) if it unexpectedly fails URL parsing.","Remove GITNEXUS_MCP_ALLOWED_REPOS if group resources are genuinely needed."],"exampleFix":"# before (allowlist set, malformed URI)\nread_resource 'gitnexus:\\/\\/group\\/org\\/contracts'\n\n# after\nread_resource 'gitnexus://repo/frontend/context'","handlingStrategy":"validation","validationCode":"function isGroupShaped(uri: string): boolean {\n  try {\n    new URL(uri);\n    return false; // parseable — handled by the well-formed branch\n  } catch {\n    return /^gitnexus:\\/\\/group(?:\\/|$)/iu.test(uri);\n  }\n}\nif (restricted && isGroupShaped(uri)) {\n  throw new Error('Malformed group-shaped URI under allowlist — use gitnexus://repo/{name}/...');\n}","typeGuard":"const isWellFormedUri = (uri: string): boolean => {\n  try { new URL(uri); return true; } catch { return false; } };","tryCatchPattern":"try {\n  await client.readResource({ uri });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Group resources are unavailable')) {\n    return null; // skip group-shaped entries when iterating under an allowlist\n  }\n  throw e;\n}","preventionTips":["Always build URIs with a URL/template helper instead of raw string concatenation with escapes.","Test resource URIs with new URL(...) client-side before sending.","Remember: under an allowlist, even well-formed group URIs are rejected — malformed ones fail closed too."],"tags":["mcp","allowlist","resources","group-routing","uri-validation"],"backgroundTag":"allowlist-restriction","analyzedSha":"52924ef12c2290ceee4612526a828ec4cdf2047f","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}