A missing first article is an inspection result, not a failure
Test and deploy / test (push) Successful in 1m35s
Test and deploy / deploy (push) Successful in 1m59s

Eweka answers 'no such article' after ~450 ms; a quarter of inspection
requests were for missing articles and each failed job was retried up
to three times. Files get content_type 'missing' instead; homelab also
records it from failure reports of nodes not rebuilt yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daan MeijerandClaude Opus 5.5 committed 2026-10-06 23:54:01 +02:00
1 parent bef68075be
commit 61192690a7
6 files changed
+57 -5

No files matched your search

+16 -1
View File
@@ -83,6 +83,11 @@ export function chunkMd5(data: Buffer, fileSize: number): string | undefined {
return createHash('md5').update(data.subarray(0, FIRST_CHUNK)).digest('hex')
}
/** A "no such article" answer: 430 from the news server, 410 from the seed relay. */
export function isMissingArticle(error: unknown): boolean {
return /(^|[^0-9])(430|410)([^0-9]|$)/.test(String((error as any)?.message ?? error)) && !/timed out|no answer/i.test(String((error as any)?.message))
}
/** Decodes a body; some tools declare a size that leaves out a header (nzbe), the CRC still has to match. */
export function decodeBody(body: Buffer): DecodedSegment {
try {
@@ -132,7 +137,17 @@ export async function probeInspection(
fetch: BodyFetcher,
warn: (message: string) => void = () => {}
): Promise<InspectProbe> {
const segment = decodeBody(await fetch(task.segments[0].id))
let body: Buffer
try {
body = await fetch(task.segments[0].id)
} catch (error: any) {
// The server no longer has it (430; 410 through the seed relay), typically a takedown. A final
// answer, and an expensive one: Eweka takes ~450 ms per missing article, so it isn't retried
// (LOG.md 2026-10-06 (14)).
if (isMissingArticle(error)) return { size: 0, contentType: 'missing', md5_16k: null, yencName: '' }
throw error
}
const segment = decodeBody(body)
const size = segment.meta.begin.size
const contentType = sniffContentType(segment.data)
const probe: InspectProbe = {