Files
pgsql-jellyfin/FIX_EMPTY_DATABASE_XML.md
T
wjones 69c213f13c Auto-generate startup.json and add database presets
Introduces automatic creation of a self-documenting startup.json file on first startup, with example path configurations for Linux, Windows, and portable setups. Adds preset database configurations to database.xml for easy switching between SQLite and PostgreSQL. Refactors database config classes for XML serialization compatibility and ensures database.xml is never empty. Comprehensive documentation and migration guides included.
2026-02-25 16:29:08 -05:00

6.3 KiB

Fix: Empty database.xml on Startup

Problem

The database.xml file was being created empty on startup, causing Jellyfin to fail or not properly initialize the database configuration.

Root Cause

Issue 1: required Keyword Incompatibility

The DatabaseConfigurationOptions class had the DatabaseType property marked with the C# 11 required keyword:

public required string DatabaseType { get; set; }

Problem: The System.Xml.Serialization.XmlSerializer used by Jellyfin doesn't support the required keyword. When trying to serialize/deserialize:

  • XML serializer couldn't create instances with required properties
  • Serialization would fail silently or create empty files
  • Deserialization from empty files would fail

Issue 2: Dictionary<string, T> Not XML-Serializable

The PresetConfigurations property used a Dictionary<string, PresetDatabaseConfiguration>:

public Dictionary<string, PresetDatabaseConfiguration>? PresetConfigurations { get; set; }

Problem: Dictionary<TKey, TValue> is not directly XML-serializable by XmlSerializer without custom serialization logic.

Solution Applied

Fix 1: Removed required Keyword

Changed DatabaseType to nullable and added default value in constructor:

Before:

public required string DatabaseType { get; set; }

After:

public DatabaseConfigurationOptions()
{
    DatabaseType = "Jellyfin-SQLite";
    LockingBehavior = DatabaseLockingBehaviorTypes.NoLock;
}

public string? DatabaseType { get; set; }

Fix 2: Replaced Dictionary with XML-Serializable List

Changed from Dictionary<string, T> to List<PresetConfigurationItem> with XML attributes:

Before:

public Dictionary<string, PresetDatabaseConfiguration>? PresetConfigurations { get; set; }

After:

[XmlArray("PresetConfigurations")]
[XmlArrayItem("PresetConfiguration")]
public List<PresetConfigurationItem>? PresetConfigurations { get; set; }

New supporting class:

public class PresetConfigurationItem
{
    [XmlElement("key")]
    public string? Key { get; set; }

    [XmlElement("value")]
    public PresetDatabaseConfiguration? Value { get; set; }
}

Fix 3: Added Null Check Safety

Added validation to ensure DatabaseType is never null when reading configuration:

// Ensure DatabaseType is set (handle corrupted or empty configuration files)
if (string.IsNullOrWhiteSpace(efCoreConfiguration.DatabaseType))
{
    efCoreConfiguration.DatabaseType = "Jellyfin-SQLite";
    configurationManager.SaveConfiguration("database", efCoreConfiguration);
}

Files Modified

  1. src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/DatabaseConfigurationOptions.cs

    • Removed required keyword
    • Added default constructor with defaults
    • Changed Dictionary to List with XML attributes
  2. src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/PresetConfigurationItem.cs (NEW)

    • Created new class for XML-serializable key-value pairs
    • Added proper XML attributes
  3. Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs

    • Updated preset creation to use List<PresetConfigurationItem>
    • Added null check for DatabaseType

XML Output

The database.xml file now properly serializes:

<?xml version="1.0" encoding="utf-8"?>
<DatabaseConfigurationOptions>
  <DatabaseType>Jellyfin-SQLite</DatabaseType>
  <LockingBehavior>NoLock</LockingBehavior>
  <PresetConfigurations>
    <PresetConfiguration>
      <key>SQLite</key>
      <value>
        <DatabaseType>Jellyfin-SQLite</DatabaseType>
        <LockingBehavior>NoLock</LockingBehavior>
        <Description>Default SQLite database configuration. Database stored in data directory.</Description>
      </value>
    </PresetConfiguration>
    <PresetConfiguration>
      <key>PostgreSQL-Local</key>
      <value>
        <DatabaseType>Jellyfin-PostgreSQL</DatabaseType>
        <LockingBehavior>NoLock</LockingBehavior>
        <Description>PostgreSQL database on localhost. Update CustomProviderOptions with your database credentials.</Description>
      </value>
    </PresetConfiguration>
  </PresetConfigurations>
</DatabaseConfigurationOptions>

Testing

  1. Delete existing database.xml (if any)
  2. Start Jellyfin
  3. Verify database.xml is created in config directory
  4. Check file contains proper XML structure (not empty)
  5. Confirm Jellyfin uses SQLite by default
  6. Test switching to PostgreSQL by editing DatabaseType

Verification Commands

Windows (PowerShell)

# Check if file exists and has content
Get-Content "$env:LOCALAPPDATA\jellyfin\config\database.xml"

# Check file size (should be > 0 bytes)
(Get-Item "$env:LOCALAPPDATA\jellyfin\config\database.xml").Length

Linux

# Check if file exists and has content
cat ~/.config/jellyfin/database.xml

# Check file size
ls -lh ~/.config/jellyfin/database.xml

Expected Behavior

database.xml created on first startup
Contains proper XML with DatabaseType, LockingBehavior, and PresetConfigurations
Defaults to SQLite if no configuration exists
Can be edited to switch between databases
Presets visible as templates in the file

Backward Compatibility

Existing configurations will be loaded correctly
Old database.xml files without presets will still work
No data migration needed
Graceful degradation - if file is corrupted, defaults to SQLite

This fix resolves:

  • Empty database.xml files on startup
  • Configuration not persisting
  • Database provider selection not working
  • Preset configurations not saving

Prevention

To prevent similar issues in future:

  1. Avoid using required keyword with XML-serialized classes
  2. Use XML-serializable types (avoid Dictionary, use List instead)
  3. Add default constructors to serialized classes
  4. Test XML serialization/deserialization in unit tests
  5. Add null checks when reading configuration

See Also