{"record":{"id":"74057e2dffc7f5f0","repo":"abhigyanpatwari/GitNexus","slug":"interface-dispatch-members-over-the-fan-out-cap-d","errorCode":null,"errorMessage":"interface-dispatch: members over the fan-out cap dropped implementors (their CALLS edges were not emitted)","messagePattern":"interface-dispatch: members over the fan-out cap dropped implementors \\(their CALLS edges were not emitted\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"gitnexus/src/core/ingestion/scope-resolution/pipeline/run.ts","lineNumber":1054,"sourceCode":"          recordResolutionOutcome,\n          calleeIdSink: calleeIdAccumulator,\n          // The pass's only source of positive EXTERNAL evidence for a dropped\n          // receiver (`console.log`, `fetch(...)`). Same hook, same spelling as\n          // the `emitFreeCallFallback` wiring below.\n          isBuiltInName: provider.languageProvider.isBuiltInName,\n          // What each heritage clause instantiated its base with, so the\n          // interface-dispatch fan-out can refuse an incompatible instantiation\n          // (#2912). Empty under `callableFlowOnly`, which emits no dispatch.\n          heritageTypeArguments,\n        },\n      );\n  const receiverExtras = receiverBound.emitted;\n  if (receiverBound.dispatchFanoutSkipped > 0) {\n    // Never drop dispatch coverage silently (#2829) — same contract as the\n    // property-dispatch cap below. An interface member over the cap loses real\n    // implementors, so `impact()` on those implementations under-reports; an\n    // operator has to be able to see WHICH member lost them.\n    logger.warn(\n      {\n        lang: provider.language,\n        dispatchFanoutSkipped: receiverBound.dispatchFanoutSkipped,\n        dispatchFanoutSkippedNames: receiverBound.dispatchFanoutSkippedNames,\n        fanoutCap: MAX_INTERFACE_DISPATCH_FANOUT,\n      },\n      'interface-dispatch: members over the fan-out cap dropped implementors (their CALLS edges were not emitted)',\n    );\n  }\n  const unresolvedReceiverExtras =\n    !callableFlowOnly && provider.emitUnresolvedReceiverEdges !== undefined\n      ? provider.emitUnresolvedReceiverEdges(\n          graph,\n          indexes,\n          emitParsedFiles,\n          postHeritageNodeLookup,\n          handledSites,\n          readonlyModel,","sourceCodeStart":1036,"sourceCodeEnd":1072,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/0d1aed942f0e8b5d3bac27519fff441aceea722d/gitnexus/src/core/ingestion/scope-resolution/pipeline/run.ts#L1036-L1072","documentation":"During scope-resolution emit, the interface-dispatch pass fans a call through an interface member out to CALLS edges for each of its implementors. When a member has more implementors than MAX_INTERFACE_DISPATCH_FANOUT (default 32, overridable via GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT), the overflow implementors get no CALLS edges at all. The pipeline warns (#2829 contract) so the coverage loss is visible, because impact() on those dropped implementations would otherwise silently under-report.","triggerScenarios":"Running analyze (emit phase, not callableFlowOnly) on a repo where one interface member is implemented by more than 32 types (mock proliferation is the usual cause); the receiverBound result reports dispatchFanoutSkipped > 0 and the orchestrator logs the warn with language, skipped count, skipped member names (bounded to 20 samples), and the cap.","commonSituations":"Large TypeScript/Java codebases with heavy interface mocking in tests, DI containers registering many implementors, generated code merging dozens of classes under one interface, or someone lowering the fan-out cap env below the repo's real implementor count.","solutions":["Set GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT to a value above your widest interface (e.g. 128) and re-run analyze","Read dispatchFanoutSkippedNames in the warn to find the exact member(s), then verify with impact() on the implementors after re-indexing","If the wide interface is test-mock noise, exclude generated/mock paths from indexing so the cap is not consumed by uninformative targets","Keep the override bounded — the fan-out is a per-call-site product, so huge caps multiply graph size without adding actionable information"],"exampleFix":"# before\nexport GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT=   # unset → default 32, warn fires\nnpx gitnexus analyze\n# after\nexport GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT=128\nnpx gitnexus analyze   # warn disappears; CALLS to implementors emitted","handlingStrategy":"validation","validationCode":"// Before trusting impact() results, assert the emit logs are cap-clean:\nimport { execSync } from 'node:child_process';\nconst out = execSync('npx gitnexus analyze 2>&1', { encoding: 'utf8' });\nif (out.includes('interface-dispatch: members over the fan-out cap')) {\n  throw new Error('impact() would under-report: raise GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set GITNEXUS_MAX_INTERFACE_DISPATCH_FANOUT above your widest interface's implementor count before large indexes","Grep analyze output for 'dispatchFanoutSkippedNames' and resolve every named member","Never treat an empty impact() caller list on an implementor as proof of dead code when this warn appeared in the same run"],"tags":["static-analysis","dispatch","calls-edges","graph-coverage","configuration"],"backgroundTag":"result-set-truncation","analyzedSha":"0d1aed942f0e8b5d3bac27519fff441aceea722d","analyzedAt":"2026-08-20T23:29:22.980Z","contentChangedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}