Table of Contents

Troubleshooting

Each section gives a symptom, its causes, how to tell which cause you have, and the fix. The messages quoted are the exact text the engine writes or throws. Most of them reach you only when logging is set up, so start with the first section if nothing shows.

No messages appear in the log

The game and the server log through Microsoft.Extensions.Logging. A game with no logging provider writes nothing, so none of the messages on this page can be seen. Register one in Configure, as Setting up a project shows. A server has a console provider already, at Information and above (see Logging).

Some messages are at Debug or Trace level. To see every message on this page, set the minimum level to LogLevel.Trace.

The game starts on OpenGL with a Vulkan error, or stops with one

Vulkan is the default renderer. When it cannot start, the game logs a warning with the reason, and starts on OpenGL instead:

The Vulkan render backend failed to start: <reason>. Starting on the OpenGL render backend instead.

The reason is one of these messages, which says what is missing:

  • SDL_Vulkan_LoadLibrary failed: <SDL error>. Ensure Vulkan loader/runtime is installed. The Vulkan loader is not installed. On Linux, install the distribution's Vulkan loader package.
  • No Vulkan physical devices were found. The loader is there, but no driver for a GPU is. Install the Vulkan driver for the graphics hardware, such as Mesa's Vulkan drivers on Linux. A virtual machine without GPU access usually has none.
  • No Vulkan physical device supports graphics + present for the SDL surface. A GPU has Vulkan, but none of its devices can draw to this window.
  • vkCreateInstance failed with <result>. The library found cannot start a Vulkan instance. On a Mac, see The game will not start on a Mac.

Install the missing part to run on Vulkan. Until then the game runs on OpenGL, whose name is set with --render-backend opengl on the command line, with the BSGAMELIB_RENDER_BACKEND environment variable, or with PreferredRenderBackend in the game's WindowOptions. See Render backends.

With the fallback turned off, the game stops at startup instead. The exception's message begins with The Vulkan render backend failed to start: followed by the same reason, and says that starting on OpenGL is turned off. Turn it off with AllowRenderBackendFallback = false in the game's WindowOptions, or with BSGAMELIB_RENDER_BACKEND_FALLBACK=0. See Falling back to OpenGL.

The game runs on a different renderer from the one I set

The log says which renderer was chosen, and why. Look for one of these, at Information level:

  • Using OpenGL render backend requested via environment variable BSGAMELIB_RENDER_BACKEND ('opengl').
  • Using Vulkan render backend requested via command line option ('vk').
  • Using default Vulkan render backend.

The renderer is taken from the first of these that is set: the environment variable, then the command line, then PreferredRenderBackend, then Vulkan. An environment variable or argument set in the shell therefore overrides the game's own choice. Unset it, or pass the name you want.

If Vulkan was chosen and failed to start, the log has the Vulkan warning instead, and the game runs on OpenGL. See The game starts on OpenGL with a Vulkan error.

A name the engine does not know is ignored, with this warning:

Requested render backend 'metal' via command line option is not available. Falling back to Vulkan.

The names are vulkan, vk or default for Vulkan, and opengl, open-gl or gl for OpenGL.

The game will not start on a Mac

The Vulkan renderer on macOS uses MoltenVK, which the package copies beside the game when it is published for osx-arm64 or osx-x64 (see Platforms). The messages are:

  • SDL failed to load Vulkan library (initial error: <SDL error>). Install the Vulkan/MoltenVK runtime and ensure libvulkan or libMoltenVK is on your library path. The game did not find libMoltenVK.dylib beside its executable, nor in /usr/local/lib or /opt/homebrew/lib. This happens when the game is run from a copy that lacks the library, such as a hand-made folder, or from a build that was not published for a Mac runtime. Publish for osx-arm64 or osx-x64, or install MoltenVK in one of those folders.
  • vkCreateInstance failed with ErrorIncompatibleDriver. The library that was found cannot start a Vulkan instance. Publish again for the Mac's runtime, so the game's folder gets the library for that platform.

Without MoltenVK the game starts on OpenGL, with the warning above in the log. See Render backends.

The build fails with "bsshaderc was not found"

The build error is bsshaderc was not found, so shaders cannot be precompiled. Set BSGameLibShaderCompilerPath to bsshaderc.dll. It appears only in a project with BSGameLibShader or BSGameLibRawShader items. The compiler is in the package's tools folder. The error means that folder has no compiler, which happens when the package is damaged or when the engine is referenced as a project, with no shader build project beside it.

Restore the packages again, with dotnet restore --force. If that does not help, set BSGameLibShaderCompilerPath in the project to the path of a built bsshaderc.dll. See Shaders.

A shader cannot be compiled for Vulkan at run time

The Vulkan renderer logs this warning once for each shader it cannot compile:

A Fragment shader (<key>) could not be compiled for Vulkan. A sprite or shape material that uses it is drawn by the built-in pipeline without its shader; a full-screen effect that uses it throws when it is created.

The warning is logged once per shader. A sprite or shape that uses the shader still appears, without the shader's effect. A full-screen effect that uses it is not created. The line after the warning gives the reason. When the game has no runtime compiler, it is:

No precompiled SPIR-V matches this Fragment shader (<key>), and no runtime shader compiler is registered. Precompile it at build with a BSGameLibShader item, or register one with AddShaderCompiler() from BSGameLib.Game.ShaderCompiler.

Two causes give this message:

  • The shader is read from a file, downloaded or generated, so the build never compiled it.
  • A runtime compiler is registered, and the GLSL does not compile. The compiler's diagnostics follow the warning.

A full-screen effect whose shader fails is not created. Creating it throws The shader could not be compiled for Vulkan. with the same diagnostics.

The fix is to add the shader to the project as a BSGameLibShader item, so the build compiles it. For shaders that only exist at run time, add the BSGameLib.Game.ShaderCompiler package and call AddShaderCompiler(). Both are described in Shaders. Fix the GLSL if the compiler reports an error in it.

Text in the game's own fonts is blank

Blank text in the game's fonts is not logged. There are two causes:

  • The font has no glyph for that character. The text is drawn with a gap where the character should be.
  • A TrueType font draws only characters up to U+FFFF. Most emoji, and some rare Chinese characters, are above that, so they are blank in a TrueType font whatever the font contains.

To tell them apart, open the font in a font viewer and look for the character. If it is there and above U+FFFF, the second cause applies. If it is not there, use a font that has it, and for a BMFont generate the file with that character included. See Content.

A font file is not found, and text is missing or the view fails

When the font file is not where the game looks, GetBMFont and GetTrueTypeFont return null and log a warning at Warning level, with the full path they looked at: BMFont <file> not found at <path>, returning null, or TrueTypeFont <file> not found at <path>, returning null. What happens next depends on the label:

  • A Label with a FontSize draws nothing. The warning above is the only sign.
  • A Label with no FontSize fails to build the view with Could not find font <file> for label <id>. Please make sure the file exists and is a valid font file.
  • Drawing with a missing font directly, outside a view, throws Value cannot be null. (Parameter 'font').

Font files are looked up under the content root. The causes are:

  • The path is wrong. A view's Font is a path relative to the content root.
  • The file is not copied to the output folder. Add it as a Content item with CopyToOutputDirectory.
  • The root is not the game's folder. The root is the working directory until the game sets it, so set it to AppContext.BaseDirectory. See Content.

On Linux a name in a path need not match its case, folder names included. See Content.

A font loads, but a view fails with "Could not load font"

The .fnt file is found, but one of the page images it names is not. The log then has an error, Error loading font <font file> for label <id>, with the real cause as its inner exception: Could not find file '<path>'. The view then fails with this message:

Could not load font <font file> for label <id>. Please make sure it is a valid font file.

The message does not name the page image, so read the inner exception. Copy the page images to the output folder, beside the .fnt file. The page names inside the .fnt file are relative to that folder. See Content.

Text in Japanese, Russian or another script is blank in Dear ImGui windows

Dear ImGui's built-in font has Latin letters only. The game's own text is not affected, so the symptom is only in ImGui windows.

If the font file you pass to UseFont does not exist, the log says ImGui font '<path>' does not exist; keeping the built-in font. Otherwise the cause is the missing script. Merge in a font that has the script's glyphs, with the matching constant from LanguageScripts: Japanese, Cyrillic and so on. See Dear ImGui.

Localized text shows the key instead of the words

These warnings explain it:

  • Localization key not found: <key> means the key is not in the language being looked up, and the text shows the key instead. Check the key's spelling, and that the language file has it.
  • Localization folder '<path>' does not exist; no languages discovered. means the folder that FilePath names is not there. Its default, Localization, is relative to the working directory, not the game's folder. Set FilePath to a path under AppContext.BaseDirectory, and copy the files into the output.
  • Localization file not found for language code '<code>' at path '<path>'. means a language was added, but its file is not in the folder.

If the default language and the fallback both fail to load, InitializeLanguages throws Default language '<code>' was not loaded successfully. Its fix is the same. The Problems list on LocalizationBootstrap names what is wrong with each file. See Localization.

A sound does not play

The log warns about the asset:

Audio asset '<asset>' for event '<event>' was not found.

Asset paths are looked up beside the executable first, then in the working directory. The content root does not apply to sounds. Copy the file into the output folder. Posting an event that was never registered also logs a warning. See Audio.

Packets from the other side are refused

The adapter logs Error handling packet from server. on a client, or Error handling packet from <IP address>. on a server, with an ArgumentOutOfRangeException whose message is:

Packet with hash <hash> is not recognized.

The receiving side does not know the packet type. A packet is identified by its class name, so this usually means the two builds have different packet classes: one side is an older build, or a class was renamed on one side only. Build the client and the server from the same packet classes. See Packets.

A packet arrives, and nothing happens

At Debug level the log shows the arrival, with the number of handlers that ran:

Packet of type <type> received. 0 handlers were run.

Nothing is logged as an error, because a registered packet with no handler is dropped. The cause is that no handler is registered for that packet type on the receiving side. The handler class is missing, or it is marked DoNotRegister. See Registering packets and handlers and Leaving a type out.

Two packet classes have the same name

The server stops at startup with an InvalidOperationException whose message is this, with the full names of the classes that clash:

Packet types must have distinct short names, since a packet is identified on the wire by a hash of its short name, so two would share one id. Short names shared: "<name>": <full name> and <full name>. Rename one of each pair.

A client that registers its packets itself gets the same message at RegisterPacket, or when it makes its DataPacketManager. Packets are identified by their class name, not by namespace, so rename one of them. See Packet identity.

Game state changes unpredictably when packets arrive

Packet handlers run on the networking threads, not on the game's thread. A handler that changes state the game reads every frame races with the game loop. The result is usually an intermittent crash, or a state that is half changed, and no error from the handler itself.

In a client, set HandlerRunner so that the handlers are posted to the game's IGameThread. On a server, set it in an IInitializer, or take a lock in the handlers and in Update. See Handlers and threads and Packet handlers and the loop.

The client cannot connect to the server

The client throws an IOException:

Could not connect to <address>:<port>: the connection was refused or could not be made.

The causes are that the server is not running, that the address or port is wrong, or that a firewall blocks UDP. The server uses two UDP ports: ListenPort, 55237 by default, and ListenPort plus 2. Open both. If the server's BindAddress is a loopback address such as 127.0.0.1, only clients on the server's own machine can reach it. See Ports and Firewalls.

The server does not start on Linux or macOS

The server logs Error starting game socket. at Critical level, followed by this exception:

PlatformNotSupportedException: QUIC is not available because the msquic native library is missing.

The rest of the message says where msquic comes from. The BSGameLib.MsQuic.Native package supplies it for Linux (x64 and arm64) and for Apple silicon, and a self-contained publish loads the copy beside the game. A framework-dependent run, such as dotnet run, uses an msquic installed on the system, not the copy beside the game. So either publish self-contained, with --self-contained, or install msquic from your system's packages (libmsquic on Linux). On an Intel Mac there is no bundled library, so install msquic there too. See Platforms and Platforms.

The server stops at startup because it has no certificate

The server logs Error starting game socket. and then this exception:

NetworkAdapterConfiguration.Quic.ServerCertificate is required.

This happens when QuicConfiguration is set without a ServerCertificate. Either set the certificate, or leave QuicConfiguration unset, so the server makes its own development certificate. See The host and its settings and Making a server certificate.

The server does not start because its port is in use

The server logs Error starting game socket. at Critical level. The exception beneath it is the operating system's socket error. The usual cause is another process using ListenPort or ListenPort plus 2, on UDP. Stop that process, or change ListenPort in the server's settings. See The host and its settings.

The server stops at startup and names BindAddress

The server stops with an InvalidOperationException whose message is one of these:

BindAddress "<value>" is not an IP address. Use an address such as "127.0.0.1" or "::1", or leave BindAddress unset to listen on every address. A host name is not looked up.

BindAddress "<value>" is an IPv6 address, and this host has no IPv6.

BindAddress takes an IP address literal, not a host name. Use one of those, or leave it unset to listen on every address. See The host and its settings.

Clients refuse the server after its certificate changed

A client logs a warning with this start:

QUIC server identity mismatch for <address>:<port>: expected fingerprint <A>, but the server presented <B>.

The warning ends with Rejecting connection. Clients remember the certificate that a server first presented, in quic-server-pins.json in the folder of the client's executable. A server with a different certificate is refused until that record is removed. The usual causes are that the server's certificate file was deleted, that a new certificate was installed, or that another machine answers at that address.

If the change is intended, delete that server's entry from quic-server-pins.json on the client, and the next connection records the new certificate. If it was not intended, restore the server's certificate. See Trust on first use.

The log says certificate validation was bypassed

Every connection logs this warning:

QUIC certificate validation bypassed (AllowUntrustedCertificates=true) for <server>. This connection is encrypted but NOT authenticated.

The client has AllowUntrustedCertificates set, so it does not check the server's identity at all. It is for development only. Remove it before the game is released. See Development: skip the check.

The server stops and names IGameServer as required

The server stops at startup with this exception:

Fatal exception. The following types are required to be implemented: followed by BSGameLib.Server.IGameServer.

No class implementing IGameServer was found. Write one, in an assembly the host scans. See The game loop and What is scanned.

The server stops with "Sequence contains more than one element"

The host needs exactly one class that implements IGameServer, and this is the exception when there are two. The message does not name the classes. Search the project for : IGameServer to find both, and keep one. See How it finds your code.

The server keeps running, but the world stops updating

When Update throws, the tick is skipped and the loop carries on. The first failure is logged as an error with its exception:

A game tick failed and was skipped (<n> so far). The game loop carries on.

Read the exception logged with the first failure, which says where Update threw. Later failures are logged at most once every five seconds. See Failing ticks.

A view does not open

Opening a view throws when the view is not valid. The exception is an AggregateException out of SetView, PushView or ShowPanel. Its InnerExceptions are the validation errors, such as a Button with no Size, or a ComboBox with no options. A GridLayout cell outside the grid throws an ArgumentOutOfRangeException as the view is built. If a binding cannot be set up, the log says Error setting up bindings for view <view>.

To find these before the game runs, check the view files with ViewFileValidator. See Checking views before they run.

A binding is skipped, and the control shows its default

The log warns once for each binding it skips:

Binding <Control>.<Property> to "<Key>" was skipped: <problem>.

The problem is one of the view model has no readable property with that name, the view model has no writable property with that name, or a value that cannot be converted, such as the value '<value>' cannot be read as <Type>. Check the name against the view-model's property, including its case. See When a name does not match.

The server fails to start with "attempted to register an existing keyword"

Two chat commands share a keyword. The message is:

"<command>" attempted to register an existing keyword '<keyword>' for "<other command>", .

Give one of the commands a different keyword with WithKeywords, or with the [Command] attribute's keywords. See Commands.