Kareadita/Kavita · error · KavitaException

errors.import-fields.non-unique-fields

errors.import-fields.non-unique-fields

Error message

errors.import-fields.non-unique-fields

What it means

Thrown by SettingsService.ImportFieldMappings when the FieldMappings list contains entries with duplicate Ids - DistinctBy(f => f.Id).Count() differs from total count. Field mappings are identified by Id, so two entries sharing an Id create an ambiguous import target.

Source

Thrown at Kavita.Services/SettingsService.cs:136

        // Save changes
        await unitOfWork.CommitAsync(ct);

        // Return updated settings
        return await unitOfWork.SettingsRepository.GetMetadataSettingDto(ct);
    }

    public async Task<FieldMappingsImportResultDto> ImportFieldMappings(FieldMappingsDto dto,
        ImportSettingsDto settings, CancellationToken ct = default)
    {
        if (dto.AgeRatingMappings.Keys.Distinct().Count() != dto.AgeRatingMappings.Count)
        {
            throw new KavitaException("errors.import-fields.non-unique-age-ratings");
        }

        if (dto.FieldMappings.DistinctBy(f => f.Id).Count() != dto.FieldMappings.Count)
        {
            throw new KavitaException("errors.import-fields.non-unique-fields");
        }

        return settings.ImportMode switch
        {
            ImportMode.Merge => await MergeFieldMappings(dto, settings),
            ImportMode.Replace => await ReplaceFieldMappings(dto, settings),
            _ => throw new ArgumentOutOfRangeException(nameof(settings), $"Invalid import mode {nameof(settings.ImportMode)}")
        };
    }

    /// <summary>
    /// Will fully replace any enabled fields, always successful
    /// </summary>
    /// <param name="dto"></param>
    /// <param name="settings"></param>
    /// <returns></returns>
    private async Task<FieldMappingsImportResultDto> ReplaceFieldMappings(FieldMappingsDto dto, ImportSettingsDto settings)
    {

View on GitHub (pinned to 9c3e540000)

Solutions

  1. De-duplicate FieldMappings by Id before import (keep one per Id).
  2. Regenerate the export from a clean current instance.
  3. Validate the import payload with a schema requiring unique Ids.
  4. When merging exports, reconcile conflicting Ids (assign new ids or drop duplicates).

Example fix

// before
await settingsService.ImportFieldMappings(dto, settings, ct);

// after - dedupe by Id
dto.FieldMappings = dto.FieldMappings
    .DistinctBy(f => f.Id)
    .ToList();
await settingsService.ImportFieldMappings(dto, settings, ct);
Defensive patterns

Strategy: validation

Validate before calling

if (dto.FieldMappings.DistinctBy(f => f.Id).Count() != dto.FieldMappings.Count)
    dto.FieldMappings = dto.FieldMappings.DistinctBy(f => f.Id).ToList();
await settingsService.ImportFieldMappings(dto, settings, ct);

Type guard

static bool FieldMappingIdsUnique(IEnumerable<FieldMapping> f) => f.DistinctBy(x => x.Id).Count() == f.Count();

Prevention

When it happens

Trigger: Posting a FieldMappingsDto whose FieldMappings collection has two or more items with the same Id - e.g. copy-pasted rows in an export, or a merge of two exports that both contain the same mapping id.

Common situations: Exporting the same mapping twice into one file; a UI allowed adding a duplicate row; or a migration re-imported without clearing prior ids.

Related errors


AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13). Data as JSON: /api/errors/788db780afc0b723. Report an issue: GitHub.