What's actually behind a subscription link
A subscription link is, at its core, just an HTTP/HTTPS address. The client fetches it periodically, receives a block of text, and parses that text into a node list and rule set according to some agreed-upon format. The catch is that "some agreed-upon format" isn't singular. Different proxy ecosystems each developed their own text structure independently, so under the umbrella term "subscription," at least three formats circulate widely today: the YAML config used by the Clash family, the Base64 node list used by v2ray/Shadowsocks-style tooling, and the proprietary JSON formats output by certain panel software. Whether a client can "read" a given link depends entirely on whether it implements a parser for that specific format.
This is exactly why the same subscription link can import cleanly in a Clash client, yet return a format error or an empty list in a client built on a different protocol stack — the link isn't broken; the two sides simply aren't speaking the same "language."
Clash YAML config: structured, readable, and the most capable
The subscription format used by Clash and Clash Meta (mihomo core) is plain YAML text. A complete config typically has several top-level fields:
port: 7890
socks-port: 7891
mode: rule
proxies:
- name: "HK-01"
type: ss
server: example.com
port: 443
cipher: aes-256-gcm
password: "your-password"
proxy-groups:
- name: "Auto"
type: url-test
proxies: [HK-01]
url: "http://www.gstatic.com/generate_204"
interval: 300
rules:
- DOMAIN-SUFFIX,google.com,Auto
- MATCH,DIRECT
Note that YAML doesn't just describe nodes (proxies) — it also describes how nodes are grouped (proxy-groups) and which rules (rules) route traffic to which group. That's what sets it apart from other formats: a YAML subscription is a complete, runnable policy, not just a list of addresses. But because it carries so much information, YAML is strict about field spelling and indentation depth — a single missing space, or a stray tab mixed into the indentation, can break parsing for the entire config.
Note: YAML is extremely sensitive to indentation — fields at the same level must have exactly the same number of leading spaces, and tabs must never be used for indentation. When editing config files by hand, use an editor with YAML syntax highlighting; a single missing space is very hard to spot with the naked eye.
Base64 node lists: lightweight, but just an "address list"
Another common format comes from the client ecosystems built around Shadowsocks, V2Ray, Trojan, VLESS, and similar protocols. Opening one of these subscriptions usually shows a single unbroken string of seemingly random characters, often starting with something like c3M6Ly8.... What's actually happening is that multiple node links (each in the form ss://…, vmess://…, trojan://…) are joined with line breaks and then Base64-encoded as a whole, so the result is safe to transmit through QR codes and plain-text channels without special characters getting mangled.
After decoding, each line has roughly this structure (using Shadowsocks as an example):
ss://[email protected]:443#HK-01
vmess://eyJ2IjoiMiIsInBzIjoiSEstMDIiLCJhZGQiOiJleGFtcGxlLmNvbSJ9
trojan://[email protected]:443?sni=example.com#HK-03
This format carries far less information than YAML: it only describes which nodes exist, their addresses, and the encryption used — no grouping policy, no routing rules. Once the client has this list, how the rules are defined and how nodes get grouped is entirely up to the client's own built-in defaults. This explains a common source of confusion: the same Base64 subscription, imported into two different clients, ends up with the same number of nodes but completely different group structures and default routing — because grouping logic was never part of the subscription content to begin with; the client fills it in on its own.
Why some links import with no nodes, or fail outright
Given the differences above, import failures generally fall into a handful of categories:
- Format mismatch. Feeding a plain Base64 node list into a client expecting Clash YAML means the parser tries to read gibberish as structured YAML, and fails immediately. The reverse — trying to Base64-decode a YAML config — produces equally meaningless output.
- Unsupported protocol field. If the
typefield in the YAML specifies a protocol (or a newer transport-layer extension parameter) that an older client core doesn't yet implement, that node gets skipped, or in some cases the whole config fails to parse — the error message usually points to the specific line and field. - User-Agent filtering. Some subscription servers vary their response based on the request's User-Agent, returning empty content or an error page to requests that look like they're not coming from an official client — which shows up as zero nodes after import.
- Encoding or line-break issues. When Base64 content is copied and pasted by hand, stray line breaks, extra spaces, or an incomplete copy can truncate the decoded result, leaving some nodes missing or breaking the parse entirely.
- The link itself has expired. If the subscription provider's address is dead, or the account has hit a data cap and the server is returning restricted content, the client can't fix that on its own — you'll need to contact the provider.
A sensible order of checks: first confirm the subscription link loads and shows content in a browser; then look at what the returned content starts with (YAML typically begins with port: or proxies:, while a Base64 list is one long string with no spaces); finally, check the client's log page — most parse errors report the exact line number.
How to convert between formats
If the subscription format you have doesn't match what your client expects, the usual approach is to run it through a conversion tool rather than editing it by hand. The community widely relies on open-source conversion services such as subconverter: you feed in the original subscription link, and it outputs a new link in the target format (Clash YAML, a generic Base64 list, etc.). The client subscribes to this converted link directly, and future updates flow through automatically.
The two most common conversion directions
- Base64 node list → Clash YAML: the conversion service parses the protocol parameters out of each
ss:///vmess:///trojan://link, populates theproxiesfield, and then fills in a default set ofproxy-groupsandrulesbased on the target client type (some services let you pick a specific rule template). - Clash YAML → generic Base64 list: the service keeps only the node information from
proxies, re-encodes it as protocol-specific links, and Base64-encodes the result. Grouping and rule information is lost at this step, since the target format has no way to express rules in the first place.
Note: Converting from a structured format down to a plain node list is a one-way loss of information — routing rules and grouping logic don't survive the trip. That's fine for a quick, temporary switch to another client. But if you find yourself relying on the converted link long-term, it's better to ask your subscription provider directly for a link in the native format you need, rather than leaning on repeated re-conversion.
Safety and stability notes for conversion
A conversion service is essentially a middle-man proxy — your original subscription content passes through the conversion server before reaching your client. A few things worth keeping in mind:
- Prefer a self-hosted or trusted conversion backend. A public conversion server can see the raw subscription content that passes through it. If your subscription contains account-sensitive parameters, favor an open-source, self-deployable conversion service, or use only the official conversion endpoint offered by your subscription provider.
- Verify the node count after conversion. Individual protocol parameters that aren't recognized during conversion can get silently dropped. Compare the node count before and after; if there's a noticeable gap, check the conversion logs or try a different rule template.
- Match the rule template to your client version. Different rule templates have different grouping conventions (whether streaming gets its own group, whether certain regional groups are built in, and so on). Pick a template that matches how you actually use your client, or you'll end up with grouping that doesn't match your expectations.
- Converted links still need refreshing on the same cycle. Conversion services typically pull the original subscription live and convert it on the fly. When the original subscription expires or changes, the converted link becomes stale right along with it and needs updating too.
Three checks to run before importing
If you're not sure what format a subscription link is in, run through this quick sequence instead of repeatedly guessing and re-importing:
- Open the subscription link directly in a browser and look at how the returned content starts — fields like
port,proxies, ormixed-portpoint to Clash YAML; one long string with no line breaks points to a Base64 node list. - Check which core your client is running (Clash Meta / mihomo cores generally have broader compatibility and support more protocol types); if the core is outdated, update the client first before importing.
- If the format genuinely doesn't match what your client expects, use a conversion tool, following the safety guidance from the previous section when picking a backend.
Working through these three steps will pinpoint the actual cause behind most "import failed" or "empty node list" problems, without endless trial and error.