mihomo-party-org/clash-party · error · Error

Invalid latest.yml from update source

Error message

Invalid latest.yml from update source

What it means

checkUpdate (src/main/resolve/autoUpdater.ts:112) downloads latest.yml as text and parses it with YAML parse into IAppVersion. Error pages from proxies can still parse as a YAML object (e.g. `404: Not Found`), so the code validates that the result exists and latest.version is a string before compareVersions (which would crash on undefined.replace). Anything failing this shape check throws 'Invalid latest.yml from update source'.

Source

Thrown at src/main/resolve/autoUpdater.ts:112

    throw new Error(`GitHub Release does not provide a SHA-256 digest for "${file}"`)
  }
  return match[1].toLowerCase()
}

export async function checkUpdate(): Promise<IAppVersion | undefined> {
  const [{ 'mixed-port': mixedPort = DEFAULT_MIHOMO_PORTS.mixed }, { githubProxy = '' }] =
    await Promise.all([getControledMihomoConfig(), getAppConfig()])
  const githubUrl =
    'https://github.com/mihomo-party-org/mihomo-party/releases/latest/download/latest.yml'
  const res = await tryDownload(buildDownloadUrls(githubUrl, githubProxy), {
    headers: { 'Content-Type': 'application/octet-stream' },
    proxy: updaterProxy(mixedPort),
    responseType: 'text'
  })
  const latest = parse(res.data as string) as IAppVersion
  // 错误页也能被 YAML 解析成对象(如 `404: Not Found`),不校验会让 compareVersions 崩在 undefined.replace
  if (!latest || typeof latest.version !== 'string') {
    throw new Error('Invalid latest.yml from update source')
  }
  const currentVersion = app.getVersion()
  if (compareVersions(latest.version, currentVersion) > 0) {
    return latest
  } else {
    return undefined
  }
}

// 1:新 -1:旧 0:相同
function compareVersions(a: string, b: string): number {
  const parsePart = (part: string) => {
    const numPart = part.split('-')[0]
    const num = parseInt(numPart, 10)
    return isNaN(num) ? 0 : num
  }
  const v1 = a.replace(/^v/, '').split('.').map(parsePart)
  const v2 = b.replace(/^v/, '').split('.').map(parsePart)

View on GitHub (pinned to 911e090537)

Solutions

  1. Check/clear the githubProxy setting so latest.yml is fetched from a reachable, correct source
  2. Open the latest.yml URL in a browser and confirm it returns YAML containing `version: x.y.z`
  3. If 404, verify the release/tag still exists on the update source
  4. Retry later if the proxy is rate-limiting or temporarily serving error pages
  5. Ensure compareVersions-like consumers always run after this validation (already enforced by the throw)

Example fix

// before
const latest = parse(res.data as string) as IAppVersion
compareVersions(latest.version, currentVersion) // crashes on '404: Not Found' page
// after
const latest = parse(res.data as string) as IAppVersion
if (!latest || typeof latest.version !== 'string') {
  throw new Error('Invalid latest.yml from update source')
}
Defensive patterns

Strategy: validation

Validate before calling

const text = res.data as string
const latest = parse(text) as IAppVersion
if (!latest || typeof latest.version !== 'string') {
  throw new Error('Invalid latest.yml from update source')
}

Type guard

function isValidAppVersion(v: unknown): v is IAppVersion {
  return !!v && typeof v === 'object' && typeof (v as IAppVersion).version === 'string'
}

Try / catch

try {
  const latest = await checkUpdate()
  if (latest) promptUpdate(latest)
} catch (e) {
  if (e instanceof Error && e.message === 'Invalid latest.yml from update source') {
    // proxy served an error page; skip this cycle or retry with another source
    console.warn('update check skipped:', e.message)
  }
}

Prevention

When it happens

Trigger: The latest.yml endpoint returns an error page (404/403 HTML or text like `404: Not Found`) that YAML-parses into an object without a string `version` field; the response is empty, or valid YAML whose top level has no version key.

Common situations: A proxy mirror (githubProxy) serves its own error/rate-limit page instead of latest.yml; the release was deleted so latest.yml 404s; a captive portal or firewall injects an HTML block page; the update source URL is misconfigured to a non-GitHub host.

Related errors


AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30). Data as JSON: /api/errors/6d47c8f49b3ed9ea. Report an issue: GitHub.