elsa-workflows/elsa-core · error · InvalidOperationException

OpenTelemetry gRPC ingestion is enabled, but no gRPC…

Error message

OpenTelemetry gRPC ingestion is enabled, but no gRPC endpoint path was configured.

What it means

MapOpenTelemetryGrpcCollector is a startup-time endpoint mapper for the OpenTelemetry diagnostics module. If OpenTelemetryDiagnosticsOptions.EnableGrpc is true but GrpcEndpointPath is null/whitespace, the module has no route to bind the OTLP gRPC collector, so it throws InvalidOperationException during endpoint mapping rather than silently skipping ingestion.

Solutions

  1. Set GrpcEndpointPath to a valid path, e.g. o.GrpcEndpointPath = "/opentelemetry/proto/collector/trace/v1/traces";.
  2. If gRPC ingestion is not needed, explicitly set EnableGrpc = false so the mapper short-circuits.
  3. Verify the configuration binding key in appsettings/environment matches the GrpcEndpointPath property.

Example fix

// before
services.AddOpenTelemetryDiagnostics(o => o.EnableGrpc = true);

// after
services.AddOpenTelemetryDiagnostics(o =>
{
    o.EnableGrpc = true;
    o.GrpcEndpointPath = "/opentelemetry/proto/collector/trace/v1/traces";
});
Defensive patterns

Strategy: validation

Validate before calling

var otel = app.Services.GetRequiredService<IOptions<OpenTelemetryDiagnosticsOptions>>().Value;
if (otel.EnableGrpc && string.IsNullOrWhiteSpace(otel.GrpcEndpointPath))
    throw new InvalidOperationException("Set GrpcEndpointPath when EnableGrpc is true.");

Prevention

When it happens

Trigger: Configuring services.AddOpenTelemetryDiagnostics(o => o.EnableGrpc = true) (or setting EnableGrpc:true in appsettings) without setting GrpcEndpointPath, then calling MapOpenTelemetryGrpcCollector on the endpoint route builder.

Common situations: Partially copied sample configuration; appsettings section present but GrpcEndpointPath omitted; enabling gRPC ingestion via environment variables while the path key is missing or misspelled.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/ba93a13f448bc839. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Diagnostics.OpenTelemetry/Extensions/EndpointRouteBuilderExtensions.cs:55

        {
            return await IngestAsync(httpContext, ingestor, options, payload => OtlpHttpProtobufParser.ParseMetrics(payload.Span), cancellationToken);
        });

        endpoints.MapPost($"{basePath}/logs", static async (HttpContext httpContext, IOpenTelemetryIngestor ingestor, IOptions<OpenTelemetryDiagnosticsOptions> options, CancellationToken cancellationToken) =>
        {
            return await IngestAsync(httpContext, ingestor, options, payload => OtlpHttpProtobufParser.ParseLogs(payload.Span), cancellationToken);
        });
    }

    public static void MapOpenTelemetryGrpcCollector(this IEndpointRouteBuilder endpoints)
    {
        var options = endpoints.ServiceProvider.GetRequiredService<IOptions<OpenTelemetryDiagnosticsOptions>>().Value;

        if (!options.EnableGrpc)
            return;

        if (string.IsNullOrWhiteSpace(options.GrpcEndpointPath))
            throw new InvalidOperationException("OpenTelemetry gRPC ingestion is enabled, but no gRPC endpoint path was configured.");

        // The actual gRPC service binding is host-specific. This module exposes shared ingestion
        // contracts and accurate collector metadata without forcing every host to reference gRPC.
    }

    private static async Task<IResult> IngestAsync(
        HttpContext httpContext,
        IOpenTelemetryIngestor ingestor,
        IOptions<OpenTelemetryDiagnosticsOptions> options,
        Func<ReadOnlyMemory<byte>, OpenTelemetryBatch> parse,
        CancellationToken cancellationToken)
    {
        if (!OtlpIngestionSecurity.IsAuthorized(httpContext, options.Value))
            return Results.Unauthorized();

        try
        {
            var payload = await ReadBodyAsync(httpContext, options.Value.MaxHttpRequestBodySize, cancellationToken);

View on GitHub (pinned to fe9217bdfa)