{"record":{"id":"8cd54ababd73c66b","repo":"multica-ai/multica","slug":"runtime-provider-counts-do-not-match-runtime-count","errorCode":null,"errorMessage":"runtime provider counts do not match runtime_count","messagePattern":"runtime provider counts do not match runtime_count","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"server/internal/handler/client_usage.go","lineNumber":216,"sourceCode":"\n\tif probe.RuntimeCount == nil || probe.ProviderSummary == nil || probe.OnlineCount == nil || probe.OfflineCount == nil {\n\t\treturn validatedRuntimeProbe{}, errors.New(\"successful runtime probes require all counts\")\n\t}\n\tif *probe.RuntimeCount < 0 || *probe.RuntimeCount > 1000 || *probe.OnlineCount < 0 || *probe.OfflineCount < 0 || *probe.OnlineCount+*probe.OfflineCount != *probe.RuntimeCount {\n\t\treturn validatedRuntimeProbe{}, errors.New(\"invalid runtime counts\")\n\t}\n\tif len(probe.ProviderSummary) > 32 {\n\t\treturn validatedRuntimeProbe{}, errors.New(\"too many runtime providers\")\n\t}\n\tvar providerTotal int64\n\tfor provider, count := range probe.ProviderSummary {\n\t\tif !providerNamePattern.MatchString(provider) || count < 0 || count > 1000 {\n\t\t\treturn validatedRuntimeProbe{}, errors.New(\"invalid runtime provider summary\")\n\t\t}\n\t\tproviderTotal += int64(count)\n\t}\n\tif providerTotal != int64(*probe.RuntimeCount) {\n\t\treturn validatedRuntimeProbe{}, errors.New(\"runtime provider counts do not match runtime_count\")\n\t}\n\tsummary, err := json.Marshal(probe.ProviderSummary)\n\tif err != nil {\n\t\treturn validatedRuntimeProbe{}, errors.New(\"invalid runtime provider summary\")\n\t}\n\tvalidated.RuntimeCount = pgtype.Int4{Int32: *probe.RuntimeCount, Valid: true}\n\tvalidated.ProviderSummary = summary\n\tvalidated.OnlineCount = pgtype.Int4{Int32: *probe.OnlineCount, Valid: true}\n\tvalidated.OfflineCount = pgtype.Int4{Int32: *probe.OfflineCount, Valid: true}\n\treturn validated, nil\n}\n","sourceCodeStart":198,"sourceCodeEnd":228,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/handler/client_usage.go#L198-L228","documentation":"The final cross-check in validateClientUsageRuntime: the sum of all per-provider counts in provider_summary must equal runtime_count exactly. The summary is meant to be a complete partition of the runtimes by provider, not a top-N; if the buckets do not add up to the whole, the report is self-contradictory and rejected. This runs after per-entry validation, so counts are already known non-negative and bounded.","triggerScenarios":"POST a success probe with runtime_count=10 but provider_summary {\"openai\":6,\"anthropic\":3} (sums to 9); dropping the smallest providers client-side to respect the 32-key cap without adjusting runtime_count; counting by provider from a different snapshot than the total.","commonSituations":"Truncating the summary for the cardinality cap (error 48) and forgetting to fold the remainder into an 'other' bucket; provider tally code skipping runtimes with unknown/empty provider; concurrent runtime registration between the two tally passes.","solutions":["Derive both the total and the per-provider map from one snapshot so they partition exactly.","When merging buckets to stay under 32 providers, add the merged remainder to an explicit 'other' key so the sum is preserved.","Assert sum(values) === runtime_count client-side before POSTing.","Make sure every runtime contributes to exactly one bucket, including unknown providers."],"exampleFix":"// before\nsummary = topProviders(runtimes, 32); // dropped tail\npayload = {runtime_count: runtimes.length, provider_summary: summary};\n\n// after\nsummary = topProviders(runtimes, 31);\nconst counted = Object.values(summary).reduce((a, b) => a + b, 0);\nsummary.other = runtimes.length - counted; // preserve the partition\npayload = {runtime_count: runtimes.length, provider_summary: summary};","handlingStrategy":"validation","validationCode":"function assertSummaryPartitionsTotal(summary, runtimeCount) {\n  const sum = Object.values(summary).reduce((a, b) => a + b, 0);\n  if (sum !== runtimeCount) {\n    throw new RangeError(`provider_summary sums to ${sum} but runtime_count is ${runtimeCount}`);\n  }\n}","typeGuard":"const summaryMatchesTotal = (summary, total) => Object.values(summary).reduce((a, b) => a + b, 0) === total;","tryCatchPattern":null,"preventionTips":["Build the summary by iterating the snapshot once, then set runtime_count = snapshot.length so they partition by construction.","When folding buckets for the 32-key cap, preserve the sum with an 'other' bucket.","Include the sum-equals-total assertion in the reporter's pre-send checks."],"tags":["validation","telemetry","invariant","http-400"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}