{"record":{"id":"d7b1779dbf5625dc","repo":"microsoft/aspire","slug":"scope-limit-of-telemetryrepositorylimits-maxscopecount-d7b177","errorCode":null,"errorMessage":"Scope limit of {TelemetryRepositoryLimits.MaxScopeCount} reached. Scope '{incomingScope.Name}' will not be added.","messagePattern":"Scope limit of (.+?) reached\\. Scope '(.+?)' will not be added\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/Aspire.Dashboard/Otlp/Storage/SqliteTelemetryRepository.Caching.cs","lineNumber":228,"sourceCode":"                    FROM telemetry_scopes s\n                    LEFT JOIN telemetry_scope_attributes a ON a.scope_id = s.scope_id\n                    WHERE s.scope_name = @ScopeName\n                    ORDER BY a.ordinal;\n                    \"\"\", new { ScopeName = incomingScope.Name }, transaction);\n                if (existingRecords.FirstOrDefault() is { } existing)\n                {\n                    var attributes = existingRecords\n                        .Where(record => record.AttributeKey is not null)\n                        .Select(record => KeyValuePair.Create(record.AttributeKey!, record.AttributeValue!))\n                        .ToArray();\n                    cachedScope = GetOrAddCachedScope(existing.ScopeId, existing.ScopeName, existing.ScopeVersion, attributes);\n                }\n                else\n                {\n                    var scopeCount = connection.QuerySingle<int>(\"SELECT COUNT(*) FROM telemetry_scopes;\", transaction: transaction);\n                    if (scopeCount >= TelemetryRepositoryLimits.MaxScopeCount)\n                    {\n                        throw new InvalidOperationException($\"Scope limit of {TelemetryRepositoryLimits.MaxScopeCount} reached. Scope '{incomingScope.Name}' will not be added.\");\n                    }\n\n                    var scopeId = connection.QuerySingle<long>(\"\"\"\n                        INSERT INTO telemetry_scopes (scope_name, scope_version)\n                        VALUES (@ScopeName, @ScopeVersion)\n                        RETURNING scope_id;\n                        \"\"\", new { ScopeName = incomingScope.Name, ScopeVersion = incomingScope.Version }, transaction);\n                    var attributes = incomingScope.Attributes\n                        .Select((attribute, ordinal) => (Ordinal: ordinal, attribute.Key, attribute.Value))\n                        .ToArray();\n                    SqliteBatchInsert.BatchInsertRows(\n                        connection,\n                        transaction,\n                        attributes,\n                        MaxMetadataAttributeBatchSize,\n                        \"telemetry_scope_attributes\",\n                        [\"scope_id\", \"ordinal\", \"attribute_key\", \"attribute_value\"],\n                        (attribute, parameters) =>","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Dashboard/Otlp/Storage/SqliteTelemetryRepository.Caching.cs#L210-L246","documentation":"The SQLite telemetry repository caps the total number of instrumentation scopes stored in telemetry_scopes at TelemetryRepositoryLimits.MaxScopeCount. GetOrAddCachedScope throws this InvalidOperationException when inserting a new scope would exceed the limit; the scope is not added and its telemetry is dropped. This mirrors the in-memory scope limit (error 322) on the persistence layer.","triggerScenarios":"GetOrAddCachedScope throws when SELECT COUNT(*) FROM telemetry_scopes >= MaxScopeCount and a scope not already in the cache (by name/version) is added, reached via cachedScope or AddMetricsToDatabaseAsync. Triggered on metric ingestion when telemetry carries more distinct InstrumentationScope identities than the limit.","commonSituations":"Applications creating Meters with dynamic names (per-request, per-tenant); many libraries each shipping their own instrumentation scope converging on one Dashboard; long-lived sessions where new scope versions accumulate after deployments; third-party instrumentation generating unique scope versions per release.","solutions":["Fix producers to use a bounded set of stable scope names/versions (static Meter/ActivitySource names).","Aggregate or filter telemetry upstream (collector) so fewer distinct scopes reach the repository.","Identify which exporter/libraries contribute the most scopes and consolidate their instrumentation definitions."],"exampleFix":"// before: dynamic meter name\nvar meter = new MeterFactory.Create($\"Metrics.{tenantId}\");\n// after: one stable meter, tenant as an attribute\nvar meter = new MeterFactory.Create(\"MyApp.Metrics\");","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    await repository.AddMetricsToDatabaseAsync(batch);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"Scope limit of\"))\n{\n    logger.LogWarning(ex, \"Scope limit reached; scope '{Name}' dropped.\", incomingScope.Name);\n}","preventionTips":["Use a bounded set of static Meter/ActivitySource names across services.","Consolidate third-party instrumentation scopes where possible.","Cap dynamic scope creation (per-tenant/per-request meters) in application code."],"tags":["opentelemetry","limits","scope","sqlite","dashboard"],"backgroundTag":"resource-limit-exceeded","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}