Files
pgsql-jellyfin/docs/FIX_EMPTY_DATABASE_XML.md
T
wjones 8f860a8ec3 Centralize build output and generate OS-specific startup.json
- 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
2026-02-26 14:21:26 -05:00

200 lines
6.3 KiB
Markdown

# 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<string, T> Not XML-Serializable
The `PresetConfigurations` property used a `Dictionary<string, PresetDatabaseConfiguration>`:
```csharp
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:**
```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<string, T>` to `List<PresetConfigurationItem>` with XML attributes:
**Before:**
```csharp
public Dictionary<string, PresetDatabaseConfiguration>? PresetConfigurations { get; set; }
```
**After:**
```csharp
[XmlArray("PresetConfigurations")]
[XmlArrayItem("PresetConfiguration")]
public List<PresetConfigurationItem>? 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<PresetConfigurationItem>`
- Added null check for DatabaseType
## XML Output
The database.xml file now properly serializes:
```xml
<?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)
```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