{"record":{"id":"49f3637ee1ca10e1","repo":"mihomo-party-org/clash-party","slug":"profile-id-contains-html-instead-of-yaml-the","errorCode":null,"errorMessage":"Profile \"${id}\" contains HTML instead of YAML. The subscription may have returned an error page. Please re-import or update the subscription.","messagePattern":"Profile \"(.+?)\" contains HTML instead of YAML\\. The subscription may have returned an error page\\. Please re-import or update the subscription\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main/config/profile.ts","lineNumber":620,"sourceCode":"  return await parseProfileContent(id, await getProfileStr(id), item?.ageSecretKey)\n}\n\nexport async function parseProfileContent(\n  id: string | undefined,\n  content: string,\n  ageSecretKey?: string\n): Promise<IMihomoConfig> {\n  const profile = await decryptAgeContent(content, ageSecretKey, `profile \"${id || 'default'}\"`)\n\n  // 检测是否为 HTML 内容（订阅返回错误页面）\n  const trimmed = profile.trim()\n  if (\n    trimmed.startsWith('<!DOCTYPE') ||\n    trimmed.startsWith('<html') ||\n    trimmed.startsWith('<HTML') ||\n    /<style[^>]*>/i.test(trimmed.slice(0, 500))\n  ) {\n    throw new Error(\n      `Profile \"${id}\" contains HTML instead of YAML. The subscription may have returned an error page. Please re-import or update the subscription.`\n    )\n  }\n\n  try {\n    let result = parse(profile)\n    if (typeof result !== 'object') result = {}\n    return result as IMihomoConfig\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e)\n    throw new Error(`Failed to parse profile \"${id}\": ${msg}`)\n  }\n}\n\n// attachment;filename=xxx.yaml; filename*=UTF-8''%xx%xx%xx\nfunction parseFilename(str: string): string {\n  if (str.match(/filename\\*=.*''/)) {\n    const parts = str.split(/filename\\*=.*''/)","sourceCodeStart":602,"sourceCodeEnd":638,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/config/profile.ts#L602-L638","documentation":"parseProfileContent detects when a stored profile's content is actually HTML (DOCTYPE, <html> tags, or inline <style> in the first 500 chars) rather than YAML. This indicates the subscription URL returned an error page (login, captcha, 4xx/5xx HTML) that got saved as the profile, so parsing is aborted with a descriptive message telling the user to re-import.","triggerScenarios":"baseProfile or getProfile reads a profile whose content starts with '<!DOCTYPE', '<html', '<HTML', or contains a <style> tag within the first 500 characters.","commonSituations":"Subscription token expired and provider served a login page; captive portal or ISP interception page saved during import; provider rate-limited with an HTML error page; user pasted an HTML URL by mistake.","solutions":["Re-fetch the subscription: open the URL and confirm it returns YAML, then re-import or update the profile.","Check subscription authentication (token/cookie) — renew the expired link from the provider.","If behind a captive portal, complete portal login and retry the import.","Delete the corrupted profile entry and add it again from the correct URL."],"exampleFix":"// before\nawait getProfile(id) // stored content is '<!DOCTYPE html>...'\n// after\nawait fetchSub(correctUrl, options) // re-fetch valid YAML, then load the profile","handlingStrategy":"try-catch","validationCode":"const trimmed = content.trim().slice(0, 500)\nconst looksHtml = /^<!DOCTYPE|^<html/i.test(trimmed) || /<style[^>]*>/i.test(trimmed)\nif (looksHtml) {\n  // re-fetch the subscription before calling getProfile\n  await reImportSubscription(profileUrl)\n}","typeGuard":"function looksLikeHtml(content: string): boolean {\n  const t = content.trim()\n  return t.startsWith('<!DOCTYPE') || /^<html/i.test(t) || /<style[^>]*>/i.test(t.slice(0, 500))\n}","tryCatchPattern":"try {\n  const cfg = await getProfile(id)\n} catch (e) {\n  if (e.message.includes('contains HTML instead of YAML')) {\n    // subscription returned an error page: renew token and re-import\n  }\n}","preventionTips":["Validate subscription responses are YAML at import time, not just at read time.","Detect HTML/captcha/login pages when fetching subscriptions and fail fast.","Renew subscription tokens before they expire."],"tags":["html","yaml","subscription","profile"],"backgroundTag":"html-instead-of-yaml","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}