- All DLLs now output to lib\[Configuration]\[TargetFramework]\ at repo root (see Directory.Build.props) - .gitignore updated to exclude /lib/ - On first run, startup.json is auto-generated with OS-appropriate default paths (Windows, Linux, macOS, or portable) - Removes null/example config; generated config is immediately usable and clearly documented - Extensive new documentation: build output, startup.json logic, visual guides, and code proofs - Publish profile now deletes existing files for clean deploys - No breaking changes: existing startup.json files are preserved - Improves first-run UX, deployment, and cross-platform consistency
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
-
src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/DatabaseConfigurationOptions.cs
- Removed
requiredkeyword - Added default constructor with defaults
- Changed
DictionarytoListwith XML attributes
- Removed
-
src/Jellyfin.Database/Jellyfin.Database.Implementations/DbConfiguration/PresetConfigurationItem.cs (NEW)
- Created new class for XML-serializable key-value pairs
- Added proper XML attributes
-
Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
- Updated preset creation to use
List<PresetConfigurationItem> - Added null check for DatabaseType
- Updated preset creation to use
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
- Delete existing database.xml (if any)
- Start Jellyfin
- Verify
database.xmlis created in config directory - Check file contains proper XML structure (not empty)
- Confirm Jellyfin uses SQLite by default
- 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
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:
- ✅ Avoid using
requiredkeyword with XML-serialized classes - ✅ Use XML-serializable types (avoid Dictionary, use List instead)
- ✅ Add default constructors to serialized classes
- ✅ Test XML serialization/deserialization in unit tests
- ✅ Add null checks when reading configuration
See Also
- DATABASE_CONFIGURATION_GUIDE.md - How to configure database
- DATABASE_PRESETS_FEATURE.md - Preset configurations feature
- database.xml.example - Example configuration file