Tutorial ~19 min read

Clash Meta Logs: Find Connection Resets by Time and Rules (2026)

Proxy is on, the icon is green, and yet the browser, IDE, or game still throws a generic “connection reset” or the page spins forever. In Clash Meta and Mihomo, that symptom is a signal, not a mood: your logs can say whether the reset happened on a DIRECT path, on the tunnel to a remote node, or during DNS before routing even had a fair shot. This guide shows how to align a timeline (what the app did, when) with the rule that matched (first match wins) so you change the right knob—rules:, resolver policy, or upstream health—instead of re-importing the same subscription for the fifth time.

Clash Editorial Team Clash Meta · Mihomo · connection reset · logs · rule match

What people actually search: proxy on, but “connection reset” every few minutes

The browser’s copy is rarely helpful: ERR_CONNECTION_RESET, a TLS handshake that dies mid-flight, a download that stops at six percent, or a mobile game that blames the network even though the VPN icon is lit. Clash Meta (and the upstream Mihomo core) are honest about the control plane. They record which rule match they chose, which outbound (or DIRECT) they handed the flow to, and—when logging is set high enough—enough of the data path to tell whether the reset happened while still on your machine, on the way to a remote node, or because the DNS name never pointed where you thought. This page is a field manual for turning those sparse lines into a timeline you can defend in front of the YAML, not a generic lecture on TCP.

The mental split is always the same. First, you prove which policy won the routing race—first match wins, no exceptions, no politeness. Second, you ask whether the failure is consistent with that policy. A reset right after a DIRECT line usually means a domestic ISP or a CDN edge objected, not a bad Singapore chain. A reset on a Proxy line is often a sick upstream, an incompatible transport, or UDP falling off a table. A reset that appears before you can even name a target IP often implicates the resolver, FakeIP filters, or clients that still bypass your capture path. We walk those branches in order.

What “connection reset” can mean in practice

People say “reset” when the TCP session receives an unexpected RST or when TLS alert layers tear down a connection that looked healthy milliseconds earlier. Clash does not invent reset packets; it forwards what the stack reports. A common pattern is: SYN goes out, a handshake almost completes, then the far side (or a middlebox) aborts. Another pattern is long-lived HTTP/2 or QUIC sessions that the remote intentionally recycle during peak hours, which you experience as a burst of app-level retries. Your job in troubleshooting is not to diagnose the whole internet, but to answer one tight question: at the exact timestamp of the user-visible error, was this flow on DIRECT, a named proxy group, or something weird like REJECT that still logged as a try?

Keep three buckets in your head. Local scope problems (the application never actually entered TUN, never read the system proxy, or is resolving DNS outside the core) show up as missing rows in the log, not as a crisp reset line. Path problems (bad node, bad MTU, blocking UDP) show up with outbound tags and remote addresses you can compare across retries. Resolver problems (wrong A/AAAA, stale CDN mapping, or FakeIP that does not line up with fake-ip-filter) look like a correct rule and a wrong IP at the same time. Once you can place an incident in a bucket, you know whether to open the DNS article or the url-test and failover page next.

Be precise about the clock: your OS, the browser, and the core can disagree by time zone or log rotation. When correlating, copy the millisecond line from the client log, then search backward for the matching five-tuple (source port, remote IP, and protocol) in the proxy log.

Enable logging that actually names the path

Info level is often enough to see the outbound tag and a coarse reason. Debug is the tier where you start seeing the chain of events for tricky TLS or multiplexed transports, at the cost of noise. Turn debug on briefly, reproduce once, then turn it off—log disks fill quickly on busy desktops. In GUI clients, look for a dedicated “Log” or “Console” pane; in headless Mihomo setups, the same content lands on stdout or a file sink depending on the service file you wrote. Whatever the packaging, the fields you are hunting are stable: a timestamp, a target identifier (name or IP), a rule type or index, a policy, and a chain result.

If your UI only shows a silent failure, the API of the external-controller can still list live connections. That is slower than a log tail but proves whether the flow existed at all. The configuration documentation on this site uses the same vocabulary as the core; if your client renames things, map its labels back to the canonical keyword (GLOBAL, MATCH, etc.) so you are not comparing two different mental models.

Build a timeline before you touch YAML

Step one: reproduce with a single app so you are not entangling Discord, Steam, and curl at once. Step two: mark the user-visible time (a screen recording or even a stopwatch) and immediately export the logs window. Step three: find the first line that names your destination, then read downward for siblings that share the same source port. Bursts of retries are normal; what matters is whether they all show the same rule hit and the same exit node, or whether the core flips from Proxy to DIRECT between attempts because your url-test group is thrashing.

When a reset happens during an upstream switch, the timeline usually shows: healthy traffic on node A, health check flips, new TCP session starts on node B, reset if B is blackholed. That is not mysterious packet loss; it is policy doing exactly what the YAML says. The fix is in the proxy group (intervals, tolerance, fallback ordering), not a prayer in the rules section. Conversely, if every attempt—spanning minutes—hits DIRECT while you are absolutely certain the domain should be foreign, you do not have a connection reset mystery, you have a rule match you still have not found; go read the MATCH and ordering guide with the actual flattened array open side by side.

From log line to the row in rules:

Most helpful clients print either the name of the predicate (GEOSITE, DOMAIN-SUFFIX, and so on) or a zero-based index into the expanded rules: array. That index is the bridge between the engine and the text file you edit. If a remote RULE-SET explodes to hundreds of inline rows, the index might land inside that bundle—expand it temporarily, or move your override above the include so the number makes human sense. Remember: the first row that matches wins; no row below can veto it. When you know the index, you can reason about why a flow went DIRECT even though you “added a line for the domain” in the wrong place.

Sniffer complicates the picture in a good way. Once the core recovers a hostname from TLS SNI or QUIC, rules that need a domain become eligible. If your log shows an IP in one column and a recovered name in another, the next hop is the Sniffer how-to—not more blind IP-CIDR. Without names, a broad GEOIP,CN row or a default MATCH can quietly swallow traffic you meant to protect.

Field note: if you see a rule hit that references FINAL or a clone of MATCH, that is your catch-all. Traffic reaching it means nothing more specific above qualified—either because no predicate fit, or because metadata never contained the data your domain rules required.

When the log says DIRECT and the world still slams the door

A DIRECT rule match means the packet left the machine using your ordinary ISP path. Resets in that state are the same as running without any proxy. Corporate Wi-Fi, regional blocking, and CDN throttles are all still on the table. Before you “blame the node,” read the line again. If the destination IP belongs to a domestic datacenter, maybe your GEOSITE,CN or GEOIP,CN bucket is working exactly as designed and the service you care about is simply hosted inside the country. If that is not what you want, the lever is the rule that fired and any domain lists your provider merged above your manual edits.

A subtler DIRECT case is split-DNS. The app resolves a hostname through a public resolver that is not the one you tuned in the core, so the app connects to a different address class than the one your profile assumed. The fix lives in the DNS chapter of your Mihomo file—nameserver-policy, proxy-server-nameserver, and friends—not in adding another protocol line you do not need. The Meta DNS walkthrough walks the FakeIP, DoH, and policy surfaces that keep the resolver and the forwarder aligned; read it any time a rule “should” be foreign but the IP clearly is not.

When the log shows a proxy and the wire still dies

Now you are in upstream territory. If every reset correlates with a single server in the subscription, switch groups or provider—no amount of DOMAIN surgery fixes a dead VLESS reality. If resets appear only for UDP-heavy apps while TCP browsing works, you may be looking at a profile that does not pass UDP the way the product expects, or a node that throttles QUIC. Our Discord-focused UDP and TUN checklist is not the same root cause, but the debugging posture matches: read whether the app’s packets even appear in the logs you think you are following.

Multiplexed transports (gRPC, WebSocket, HTTPUpgrade) can surface resets when an impatient CDN closes idle subchannels. The proxy tag stays constant while the long poll collapses, so your timeline shows repeated re-handshakes on the same node. Mitigations are practical: a different path on the other side of your relay or chain, or simply a server that is less aggressive about idle timeout. The point of logging is to stop treating those as an unknowable connection reset and start treating them as a measurable transport behavior.

TUN, system proxy, and “I edited rules but the log is empty”

If the flow never shows up, it never hit the engine. Local admin tools, elevated binaries, and some runtimes talk straight to a physical interface. TUN mode exists to widen capture; the TUN setup guide in our library is the long-form recipe. Until capture is true, the best rules: in the world are decoration. A silent gap in the logs while the user still sees a browser error is more valuable signal than a thousand lines of connection reset text—it tells you the bug is in placement, not policy.

A linear checklist (keep the order)

1Reproduce and freeze the timeline

One app, one action, one failure window. Export the logs before buffers rotate. If you cannot get a line item, the issue is not yet narrow enough to edit YAML.

2Read the winning rule match and the outbound tag

Map the index to a concrete row, including any expanded RULE-SET. If the tag is wrong, stop reading node reviews—open rule order and move the line.

3Classify: DIRECT, REJECT, or a proxy

DIRECT resets point outward to ISP/CDN. Proxy resets follow health and failover tuning or a different node. REJECT means you should read your own ad-block rules before blaming Cloudflare.

4Compare DNS on both sides of the client

dig or your OS tool versus what the log implies. When they differ, the DNS guide is the follow-up, not a random Sniffer toggle.

5Prove capture before exotic tweaks

A populated connections table under load means your capture path is real. If it stays empty, fix TUN, firewall, or bypass settings first.

Mistakes that look like a haunted node

  • Editing rules while traffic never hit them: the reset is moot; the app took another exit.
  • Ignoring the first-match ladder: a remote bundle above your “obvious” DOMAIN line outranks it.
  • Chasing node ping while logs scream DIRECT: you will pay for a faster subscription and keep the same ISP failure.
  • Mixing DNS and routing debug in one pass: pick one track until it is disproved.
  • Leaving debug logging on for days: you miss the incident in a megabyte of scrollback.

Source code, trust, and where to install

Mihomo ships sources and changelogs so you can see exactly what a release changed in logging or Sniffer. That transparency matters when you are comparing two Clash GUIs that wrap different semver builds. If you need binaries for teammates, use the official Clash download page as the primary path; use upstream GitHub for issues, diffs, and license text rather than a moving release asset you cannot reproduce.

Summary

A connection reset behind Clash Meta is a thin symptom on top of a fat stack. Read the logs with a clock in one hand and the flattened rules: in the other. The rule match tells you the policy; the timeline tells you whether the failure is local capture, DNS disagreement, or a sick node. Change only the layer the evidence implicates, and the resets stop being a superstition—they become a metric you can drive to zero the boring way, by configuration.

Compared to guessing by latency leaderboards, this workflow rewards patience: the next time an app hiccups, you already know what to open first—the live log, not the subscription import dialog.

Download Clash for free and experience the difference

Previous & Next

Related Reading

Log shows connection reset?

Correlate the timestamp with the matched rule, split DIRECT vs proxy vs DNS, then change logs—not guesswork. Get a client with a clear log pane from our download page.

Download Free Client