# 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: ```csharp 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 Not XML-Serializable The `PresetConfigurations` property used a `Dictionary`: ```csharp public Dictionary? PresetConfigurations { get; set; } ``` **Problem:** `Dictionary` 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:** ```csharp public required string DatabaseType { get; set; } ``` **After:** ```csharp public DatabaseConfigurationOptions() { DatabaseType = "Jellyfin-SQLite"; LockingBehavior = DatabaseLockingBehaviorTypes.NoLock; } public string? DatabaseType { get; set; } ``` ### Fix 2: Replaced Dictionary with XML-Serializable List Changed from `Dictionary` to `List` with XML attributes: **Before:** ```csharp public Dictionary? PresetConfigurations { get; set; } ``` **After:** ```csharp [XmlArray("PresetConfigurations")] [XmlArrayItem("PresetConfiguration")] public List? PresetConfigurations { get; set; } ``` **New supporting class:** ```csharp 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: ```csharp // 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` - Added null check for DatabaseType ## XML Output The database.xml file now properly serializes: ```xml Jellyfin-SQLite NoLock SQLite Jellyfin-SQLite NoLock Default SQLite database configuration. Database stored in data directory. PostgreSQL-Local Jellyfin-PostgreSQL NoLock PostgreSQL database on localhost. Update CustomProviderOptions with your database credentials. ``` ## 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) ```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 ```bash # 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 ## Related Issues 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 - [DATABASE_CONFIGURATION_GUIDE.md](DATABASE_CONFIGURATION_GUIDE.md) - How to configure database - [DATABASE_PRESETS_FEATURE.md](DATABASE_PRESETS_FEATURE.md) - Preset configurations feature - [database.xml.example](database.xml.example) - Example configuration file