Skip to content

Logging blows up when LogError gets unexpected curly-brace expressions #4566

@allister-beamable

Description

@allister-beamable

Describe the Bug

In BeamableMicroService.ResolveCustomInitializationHook(), the catch block that handles a failed [InitializeServices] method constructs its log message using C# string interpolation, then passes the result to BeamableLogger.LogError as a format string:

BeamableLogger.LogError(
    $"Custom service initializer [{initializationMethod.DeclaringType?.FullName}.{initializationMethod.Name}] failed.\n" +
    $"{ex.Message}\n" +
    $"{{stacktrace}}",
    ex.StackTrace);

$"{ex.Message}" embeds the exception message directly into the format string via interpolation. If the exception message contains curly braces (e.g. from a JSON fragment, a C# format string, or any {...} text), the logging framework interprets them as additional placeholders. With only one argument supplied (ex.StackTrace), this causes a secondary IndexOutOfRangeException inside the logger, which propagates as:

An error occurred while writing to logger(s). (Index (zero based) must be greater than or equal to zero and less than the size of the argument list.)

This secondary exception completely masks the original [InitializeServices] failure, making the root cause invisible to the developer.

To Reproduce

Add an [InitializeServices] method that throws an exception whose message contains curly braces:

[InitializeServices]
public static async Task ReproLoggerFormatStringBug(IServiceInitializer initializer)
{
    throw new Exception("Simulated init failure with {curly} {braces} in message");
}

Run the microservice locally (beam project run). The displayed error will be the logger's secondary crash, not the thrown exception.

Expected Behavior

The original [InitializeServices] exception (message and stack trace) is logged clearly. The developer can see what actually went wrong.

Actual Behavior

A secondary IndexOutOfRangeException from the logging framework is displayed instead. The original exception is lost. The error repeats multiple times in the output:

Service failed to provide services. An error occurred while writing to logger(s).
(Index (zero based) must be greater than or equal to zero and less than the size of the argument list.)
(Index (zero based) must be greater than or equal to zero and less than the size of the argument list.)
(Index (zero based) must be greater than or equal to zero and less than the size of the argument list.)
   at Microsoft.Extensions.Logging.Logger.ThrowLoggingError(List`1 exceptions)
   ...
   at Beamable.Common.BeamableLogger.LogError(String error, Object[] args)
   at Beamable.Server.BeamableMicroService.ResolveCustomInitializationHook()
   at Beamable.Server.BeamableMicroService.SetupWebsocket(IConnection socket, Boolean initContent)

Suggested Fix

Pass ex.Message as a separate argument rather than interpolating it into the format string, and use a literal placeholder:

BeamableLogger.LogError(
    "Custom service initializer [{typeName}.{methodName}] failed.\n{message}\n{stacktrace}",
    initializationMethod.DeclaringType?.FullName,
    initializationMethod.Name,
    ex.Message,
    ex.StackTrace);

Metadata

  • Affected file: microservice/microservice/dbmicroservice/BeamableMicroService.cs, ResolveCustomInitializationHook()
  • Observed in: CLI 7.0.1 (cli-7.0.1 tag)
  • Operating System: macOS (arm64), confirmed also present in a Linux container environment

Additional Context

This bug was discovered during a CLI 7.x upgrade support case. An [InitializeServices] hook threw an exception whose message contained curly braces, triggering the secondary logging crash. The misleading error message significantly delayed diagnosis - the real failure was only found after commenting out the init hook entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions