From 57fe4c11d36010ace32e0bc41a75febf3187b72c Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Sep 2026 01:13:19 +0800 Subject: [PATCH] fix(downloads): treat missing site/adapter as permanent source failure torrentSource threw plain Error for a missing site or a site without an adapter definition, so submitDownload retried to max attempts, left the plan and slot reserved, and sweepReservedPlans re-enqueued it forever (agent runs stayed waiting on goal-settled). Throw SiteError (not_found / unsupported) so the plan fails and the slot is released. Needs image rebuild to take effect. Co-Authored-By: Claude Opus 5.5 --- apps/server/src/modules/acquisition-organize/ports.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/acquisition-organize/ports.ts b/apps/server/src/modules/acquisition-organize/ports.ts index f74f39d9..eef6f99b 100644 --- a/apps/server/src/modules/acquisition-organize/ports.ts +++ b/apps/server/src/modules/acquisition-organize/ports.ts @@ -10,6 +10,7 @@ import { getSiteDefinition, type ProbeResult, type SecretResolver, + SiteError, type TorrentSource, type VerifyMethod, } from "@umt/adapters"; @@ -107,11 +108,12 @@ export function createPorts( return client; }, async torrentSource({ release, site }) { - if (!site) throw new Error("站点不存在,无法获取种子"); + // 永久性条件用 SiteError:submitDownload 据此把计划判失败并释放 Slot,而不是反复重试、Slot 一直 reserved + if (!site) throw new SiteError("", "not_found", "站点不存在,无法获取种子"); const raw = (release.raw ?? {}) as Record; const siteMeta = (raw._site ?? {}) as Record; const defId = [str(siteMeta.adapterSiteId), site.key].find((k) => k && getSiteDefinition(k)); - if (!defId) throw new Error(`站点 ${site.key} 没有可用的适配器定义`); + if (!defId) throw new SiteError(site.id, "unsupported", `站点 ${site.key} 没有可用的适配器定义`); const accounts = await ctx.db .selectFrom("site_account") .select(["auth_kind", "secret_ref"])