mihomo-party-org/clash-party · error

Subscription failed: Profile missing proxies or providers

Error message

Subscription failed: Profile missing proxies or providers

What it means

After successfully parsing the remote subscription into an object, fetchAndValidateSubscription requires at least one of the top-level keys 'proxies' or 'proxy-providers'. A valid YAML object lacking both is rejected with 'Profile missing proxies or providers' so an empty or invalid config never replaces an existing profile.

Solutions

  1. Check the downloaded YAML contains top-level 'proxies:' or 'proxy-providers:'; fix the subscription source.
  2. Confirm the URL targets a Clash/mihomo-format config, not another proxy client's format.
  3. Use a subscription converter (e.g. subconverter) to translate the provider output to Clash format.
  4. Re-run fetchSub after updating; check the warn log 'reason=missing-proxies-or-providers' plus the summary field.

Example fix

// before (subscription content)
rules:
  - MATCH,DIRECT
// after
proxies:
  - name: node1
    type: ss
    server: example.com
    port: 443
rules:
  - MATCH,DIRECT
Defensive patterns

Strategy: validation

Validate before calling

const parsed = YAML.parse(text)
if (typeof parsed !== 'object' || parsed === null) throw new Error('not an object')
if (!parsed['proxies'] && !parsed['proxy-providers']) {
  throw new Error('config has no proxies or proxy-providers')
}

Type guard

function hasProxies(c: unknown): c is { proxies?: unknown[]; 'proxy-providers'?: unknown } {
  return typeof c === 'object' && c !== null &&
    ('proxies' in c || 'proxy-providers' in c)
}

Try / catch

try {
  await fetchSub(url, options)
} catch (e) {
  if (e.message.includes('missing proxies or providers')) {
    // convert the subscription to Clash format or fix the source
  }
}

Prevention

When it happens

Trigger: fetchSub returns parsed YAML that is an object but has neither 'proxies' nor 'proxy-providers' keys — e.g. a config with only rules, or a payload like `{}` after decryption.

Common situations: Provider serves a partial/empty template; user pointed the subscription at a Clash config variant without proxies (e.g. rules-only override file); provider mistakenly serves Surge/Quantumult configs with different schema.

Related errors


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

Appendix: source

Thrown at src/main/config/profile.ts:420

  const decryptedData = await decryptAgeContent(data, options.ageSecretKey, 'subscription')
  const parsed = parse(decryptedData) as Record<string, unknown> | null
  if (typeof parsed !== 'object' || parsed === null) {
    await profileLogger.warn(
      `Remote profile parse failed url=${redactedUrl} mode=${fetchMode} parsedType=${typeof parsed}`
    )
    throw new Error('Subscription failed: Profile is not a valid YAML')
  }
  await profileLogger.info(
    `Remote profile parsed url=${redactedUrl} mode=${fetchMode} summary=${parsedProfileSummary(parsed)}`
  )
  if (!parsed['proxies'] && !parsed['proxy-providers']) {
    await profileLogger.warn(
      `Remote profile validation failed url=${redactedUrl} mode=${fetchMode} reason=missing-proxies-or-providers summary=${parsedProfileSummary(
        parsed
      )}`
    )
    throw new Error('Subscription failed: Profile missing proxies or providers')
  }

  return { data, headers: responseHeaders }
}

export async function createProfile(item: Partial<IProfileItem>): Promise<IProfileItem> {
  const id = item.id || new Date().getTime().toString(16)
  const newItem: IProfileItem = {
    id,
    name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'),
    type: item.type || 'local',
    url: item.url,
    substore: item.substore || false,
    interval: item.interval || 0,
    override: item.override || [],
    useProxy: item.useProxy || false,
    allowFixedInterval: item.allowFixedInterval || false,
    autoUpdate: item.autoUpdate ?? false,

View on GitHub (pinned to 911e090537)