Kareadita/Kavita · warning · KavitaException
external-source-required
Error message
external-source-required
What it means
Thrown by StreamService.CreateExternalSource when dto.Name is null or empty. Note the mismatch: the localization key 'external-source-required' renders as 'Host is required', but the guard actually checks the Name field, not the Host (the Host-format check is a separate throw). It is a localized KavitaException surfaced as HTTP 500.
Source
Thrown at Kavita.Services/StreamService.cs:297
await unitOfWork.CommitAsync(ct);
if (!stream.Visible) return;
await eventHub.SendMessageToAsync(MessageFactory.SideNavUpdate, MessageFactory.SideNavUpdateEvent(userId),
userId, ct);
}
public async Task<ExternalSourceDto> CreateExternalSource(int userId, ExternalSourceDto dto,
CancellationToken ct = default)
{
var user = await unitOfWork.UserRepository.GetUserByIdAsync(userId,
AppUserIncludes.ExternalSources, ct);
if (user == null) throw new KavitaException("not-authenticated");
if (user.ExternalSources.Any(s => s.Host == dto.Host))
{
throw new KavitaException("external-source-already-exists");
}
if (string.IsNullOrEmpty(dto.Name)) throw new KavitaException("external-source-required");
if (!UrlHelper.StartsWithHttpOrHttps(dto.Host)) throw new KavitaException("external-source-host-format");
var newSource = new AppUserExternalSource()
{
Name = dto.Name,
Host = UrlHelper.EnsureEndsWithSlash(UrlHelper.EnsureStartsWithHttpOrHttps(dto.Host)),
ApiKey = dto.ApiKey
};
user.ExternalSources.Add(newSource);
unitOfWork.UserRepository.Update(user);
await unitOfWork.CommitAsync(ct);
dto.Id = newSource.Id;
return dto;
}View on GitHub (pinned to 9c3e540000)
Solutions
- Make the Name field required in the form and disable Save until it is non-empty.
- Validate dto.name is a non-empty string before posting.
- Fix the misleading message server-side if the requirement truly is Name (or add a Host emptiness check to match the text).
Example fix
// before
save(dto: ExternalSourceDto) { return this.http.post('streams/create-external-source', dto); }
// after
save(dto: ExternalSourceDto) {
if (!dto?.name?.trim()) return throwError(() => new Error('Name is required'));
return this.http.post('streams/create-external-source', dto);
} Defensive patterns
Strategy: validation
Validate before calling
function hasExternalSourceName(dto: ExternalSourceDto): boolean {
return !!dto?.name?.trim();
} Type guard
function isExternalSourceDtoWith(o: unknown): o is ExternalSourceDto {
return typeof o === 'object' && o !== null && typeof (o as any).name === 'string' && (o as any).name.trim().length > 0;
} Prevention
- Mark the Name field required in the form and disable Save until filled.
- Remember the server message says 'Host' but the real check is on Name.
- Trim whitespace before validating.
When it happens
Trigger: POST /api/streams/create-external-source with a dto whose Name is null, empty, or whitespace-only (the string.IsNullOrEmpty(dto.Name) branch).
Common situations: User fills in the Host/ApiKey but leaves the Name field blank; the form posts an incomplete object; a client bug drops the name property during serialization.
Related errors
- reading-list-title-required
- external-source-doesnt-exist
- external-source-already-in-use
- external-source-already-exists
- {comparison} is not applicable for {fieldName}
AI-assisted analysis of Kareadita/Kavita@9c3e540000 (2026-08-13).
Data as JSON: /api/errors/d10212649238f003.
Report an issue: GitHub.