Troubleshooting
This page starts from what you see: a node that never comes up, a client that cannot connect, a panel that shows no traffic. Each section names the log lines katana writes for that problem, what causes them and how to fix them. Log lines are quoted exactly as katana v3.0.1 writes them. Other versions word some of them differently and behave differently in places, so check yours with katana --version first.
Read Raise the log detail before you dig into a specific problem: most per-connection failures are logged only at debug level, which is off by default.
Where to look first
Section titled “Where to look first”| Symptom | Section |
|---|---|
| katana exits right after it starts | katana exits at startup |
| katana runs, but a node’s port is not open | A node never comes up |
| You saved the config file and nothing changed | A config edit had no effect |
| The port is open, but clients fail to connect | Clients cannot connect |
| Users are faster or slower than their plan | Speeds are not what you expect |
| The panel shows no traffic, or the wrong amount | Traffic does not show in the panel |
| A destination that an audit rule should block still loads | An audit rule does not block |
| Only the destinations routed through WireGuard time out | A WireGuard outbound passes no traffic |
Log lines such as node_info: GET … or report traffic: … |
Panel request errors |
… inbound handshake failures in the last 1s … |
Handshake failure warnings |
Three checks answer most questions before you read further:
-
Check the file.
katana --test -c /etc/katana/config.tomlprintsConfiguration OKorconfiguration error: …. It parses the file, builds the outbounds, checks each node’spanel_type,node_typeand routes, and checks the settings of Hysteria 2 nodes. It does not contact the panel or bind a port, soConfiguration OKsays nothing about the panel or the listener. -
Check the listeners. A node that is up holds its port. TCP-based nodes listen on TCP, and Hysteria 2 nodes on UDP:
Terminal window ss -ltnp | grep katana # VMess, VLESS, Trojan, Shadowsocksss -lunp | grep katana # Hysteria 2A node missing from this list either has not come up yet and is retrying, or has no users. Its log lines say which.
-
Read the log. katana writes to standard output. Under systemd, that is the journal, for example
journalctl -u katana -f.
How to read a log line
Section titled “How to read a log line”Every line has a UTC timestamp, a level, a target and a message:
2026-09-24T08:15:02.481233Z ERROR katana::manager::node: node 1: node_info failed: GET /api/v1/server/UniProxy/config; retrying in 4sThe target is the module that wrote the line; Raise the log detail uses it to turn detail on for one part of katana. The examples on this page leave out the timestamp and usually the target.
Different lines name a node in different ways:
| Form | Example | Used by |
|---|---|---|
node <node_id> |
node 1: listening on 0.0.0.0:443 |
Start-up and its retries, polls, reports, rebuilds, and a config edit the node refuses (node 1: config edit refused, …) |
node <Type>_<listen_ip>_<port> |
node V2ray_0.0.0.0_443: 37 inbound handshake failures … |
The handshake alert and the pre-authentication limit |
<panel_type>@<host>#<node_id> on SSPanel, <panel_type>@<host>#<node_id>/<node type> on NewV2board and V2board |
reload: added node newv2board@https://panel.example.com#1/v2ray |
Config reloads, including a refused one (reload: node …: …; keeping current config) |
In the reload form, <panel_type> is in lower case, and <node type> is the type katana asks the panel for: your node_type in lower case, or vless when a V2ray, VMess or VLESS node has enable_vless = true.
Panel request errors and reload lines never contain the panel key or the query string. A config parse error, however, quotes the line of the file it failed on, so read a log before you share it.
Raise the log detail
Section titled “Raise the log detail”katana’s log filter uses the tracing filter syntax: a default level, then optional target=level pairs separated by commas. A target matches itself and every module under it, so katana::manager=debug also covers katana::manager::node.
You can set the filter in two places:
At startup, katana takes its filter from the RUST_LOG environment variable when it holds a valid filter, and from [log].level otherwise. This suits a one-off foreground run:
RUST_LOG=info,katana::serve=debug katana -c /etc/katana/config.tomlFor a systemd service, put the variable in a drop-in file and restart the service. Restarting drops every connection.
[Service]Environment=RUST_LOG=info,katana::serve=debugkatana applies a changed [log].level on hot reload, without dropping connections. Save the file and look for reload: log level → …:
[log]level = "info,katana::serve=debug"From then on, the file wins over RUST_LOG. Remove the key when you are done; that sets the level back to info. An invalid value logs invalid log level "<value>": … and keeps the current filter.
These are the targets worth knowing:
| Target | What it logs |
|---|---|
katana::runtime |
Startup errors, config reloads, the file watcher, shutdown |
katana::manager::node |
Node start, polls, panel request errors, listener rebuilds, traffic and audit reports |
katana::manager::proxy |
The handshake failure alert (warn), streams dropped at the pre-authentication limit (debug), failed user refreshes (error) |
katana::manager |
Users skipped because their UUID is invalid (warn) |
katana::serve |
One debug line for each connection that fails its handshake or ends with an error, accept errors, the live-connection limit, and a failed Hysteria 2 listener (error) |
katana::api |
Audit rule problems: an unreadable rules file or a regex that does not compile (warn) |
katana::connector |
UDP associations that fail to open an outbound (debug) |
katana::outbound |
Direct UDP datagrams dropped because the name had no usable address (debug) |
etemenanki_protocols |
The proxy kernel: protocol cores, transports, the Hysteria 2 listener (etemenanki_protocols::hysteria) and the WireGuard tunnel (etemenanki_protocols::wireguard) |
boringtun |
WireGuard handshakes, keepalives and session changes |
katana exits at startup
Section titled “katana exits at startup”katana exits with status 1 when it cannot run at all. The last line says why:
| Log line | Cause | Fix |
|---|---|---|
failed to load config: … |
The file is missing, not UTF-8, or not valid TOML, or it has an unknown key | Run katana --test; it prints the same error with the line and column. Config file lists every key. |
failed to build outbounds: … |
An [[outbound]] entry or [dns] does not build |
See Outbounds. |
config defines no [[node]] entries |
The file has no [[node]] table |
Add one. [node] with single brackets does not count. |
node 1: unknown panel_type "…" |
panel_type is not SSpanel, NewV2board or V2board (in any case). Xboard is NewV2board. |
Fix the value. |
node 1: unknown node_type "…" |
node_type is not a known value |
Use V2ray, Trojan, Shadowsocks or Hysteria2. |
node 1: build router: … |
The node’s [node.route] does not compile |
See Routing. |
no nodes could be started |
Every node was skipped for one of the three reasons above | Fix them; --test reports the first. |
A node skipped for one of the three node 1: … reasons does not stop the others. katana exits only when none is left.
On a reload, the same errors do not skip a node: they refuse the whole save. If a [[node]] table in the saved file has an unknown panel_type or node_type, or adds a node whose routes do not compile, katana logs a line such as reload: node newv2board@https://panel.example.com#1/v3ray: unknown node_type "v3ray"; keeping current config, applies nothing from that save, and keeps every node running as it was. A node that was skipped at startup and is still broken therefore refuses every later save, even one that edits only other parts of the file, until you fix or remove its [[node]] table; see Hot reload. New routes for a node that is already running are checked by that node; see A config edit had no effect.
A node never comes up
Section titled “A node never comes up”A node keeps trying until it is up. Each attempt asks the panel for the node’s settings, asks for the user list, and binds the listener. A Hysteria 2 node with [node.hysteria].port set takes its settings from the file instead and asks the panel only for users. node 1: listening on 0.0.0.0:443 means all three steps worked.
flowchart TB
S["Attempt"] --> I{"Panel returns the node settings?"}
I -- no --> R["ERROR, then wait"]
I -- yes --> P{"Port is not 0?"}
P -- no --> R
P -- yes --> U{"Panel returns the user list?"}
U -- no --> R
U -- yes --> E{"Any users?"}
E -- no --> W["Nothing bound; the next poll tries again"]
E -- yes --> B{"Listener builds and binds?"}
B -- no --> R
B -- yes --> L["listening on ip:port"]
R -- "wait over, or the node's config saved" --> S
When a step fails, the node logs one ERROR line that names the step and the wait, for example node 1: user_list failed: GET /api/v1/server/UniProxy/user; retrying in 2s, and tries again after the wait. The first wait is 1 second, and each failure after that doubles it, up to 60 seconds or update_periodic, whichever is shorter. Every attempt asks the panel for everything afresh, so a fix in the panel is seen at the next attempt.
Start-up log lines
Section titled “Start-up log lines”All of these lines come from katana::manager::node at ERROR. Each begins with node <node_id>: and ends with ; retrying in <N>s, which the table leaves out. The node tries again after the wait, so fix the cause and watch for the next line.
| Log line | Cause | Fix |
|---|---|---|
node_info failed: GET /api/v1/server/UniProxy/config |
The request to an Xboard or V2board panel failed. The line does not say why: the host may be wrong or unreachable, the request may have timed out, or the panel may have rejected the key, the node_id or the node type. |
Send the request by hand to see the panel’s answer. Check the node type. |
node_info failed: GET /mod_mu/nodes/1/info |
The same for an SSPanel panel. | The same. |
node_info failed: /mod_mu/nodes/1/info: panel returned ret=0 |
SSPanel answered but refused, usually because of a wrong key or node_id. |
Check the muKey and the node ID in SSPanel. |
node_info failed: parse UniProxy config response or node_info failed: parse /mod_mu/nodes/1/info |
The panel answered with something that is not a node config, often an HTML page from a wrong host, a web firewall or a bot check in front of the panel. |
Send the request by hand and look at the body. host is the panel’s base URL, including https://. |
node_info failed: newV2board: server port must be > 0 |
The node has no port in Xboard or V2board. | Set the port in the panel. |
panel returned port 0 |
SSPanel describes the node with port 0. |
Set offset_port_node in custom_config, or the port in the legacy server string. |
node_info failed: invalid offset_port_node "" |
The SSPanel node’s custom_config has no offset_port_node. |
Set it to the port katana should listen on. |
node_info failed: sspanel: Shadowsocks node type is not supported |
node_type = "Shadowsocks" on SSPanel. |
Serve Shadowsocks from an Xboard or V2board panel. |
node_info failed: custom_config is empty, disable custom config |
An SSPanel node without custom_config. |
See SSPanel. |
node_info failed: newV2board: shadowsocks obfs "…" is not supported |
The panel sent an obfs value other than plain or none for a Shadowsocks node. Xboard sends its plugin as plugin instead, which katana does not read: the node starts without the plugin, and clients that use it fail. |
Remove the plugin from the node. |
user_list failed: … |
The user request failed. The node needs a user list to come up, so it binds nothing and retries. The endings are the same as for node_info failed. |
Check the panel as for node_info failed. On Xboard, user_list failed: parse UniProxy user response usually means a user without a speed limit; see A node with no users. |
panel returned no node info or panel returned no user list |
The panel, or something in front of it, answered 304 Not Modified, although katana asked without a tag. |
Send the request by hand and check what answers it. |
initial start failed: TLS node requires cert.mode = "file" |
The node uses TLS, because the panel turned it on or because it is a Trojan node, which always uses TLS, and [node.controller.cert] has no mode = "file". |
Set mode = "file", cert_file and key_file. |
initial start failed: TLS node requires cert.cert_file and cert.key_file |
mode = "file" with a path missing. |
Set both paths. |
initial start failed: No such file or directory (os error 2) |
cert_file or key_file does not exist. The line does not say which. |
Check both paths. Use absolute paths: a relative one is opened against katana’s working directory. |
initial start failed: Permission denied (os error 13) |
katana may not bind a port below 1024, or may not read the key file. | Grant CAP_NET_BIND_SERVICE or run as root, and check the key file’s owner and mode. |
initial start failed: Address already in use (os error 98) |
Another program, or another node in the same file, holds the port. | Free the port, or change it in the panel. |
initial start failed: Cannot assign requested address (os error 99) |
listen_ip is not an address of this host. |
Use an address the host has, or 0.0.0.0. |
initial start failed: node requests kernel-unsupported feature: … |
The panel asks for something katana does not serve: REALITY, VLESS XTLS flow, httpupgrade transport, splithttp transport, transport "…", shadowsocks cipher "…", or an ACME cert mode "…" or cert.reject_unknown_sni from your own file. |
Change the node in the panel, or the key in the file. Protocols lists what katana serves. |
initial start failed: … on a Hysteria 2 node |
A [node.hysteria] setting (unknown hysteria credential kind …, udp_idle_timeout must be between 2 and 600 seconds), the certificate (hysteria2 node requires cert.mode = "file"), or the obfuscation (unknown obfs …, obfs_password must be at least 4 bytes for salamander, obfs_password is set but obfs is not; …). Xboard sends the obfuscation password even when obfuscation is turned off, which produces the last one. |
Run katana --test, which checks the local settings. It cannot check obfuscation that comes from the panel: turn obfuscation on in the panel, or clear its password. See Hysteria 2 nodes. |
A node with no users
Section titled “A node with no users”A node whose user list is empty comes up without a listener and writes no line about it. It binds nothing, because there is nobody to serve, and binds at the first poll that returns a user, update_periodic seconds later (60 by default). Check what the panel sends:
- Xboard lists only users in one of the node’s permission groups who are not banned, not expired and within their traffic quota. A node with no permission group gets no users at all.
- Xboard,
user_list failed: parse UniProxy user response; retrying in <N>s. Xboard sends"speed_limit": nullfor a user whose plan has no speed limit, and katana rejects the whole list because of it. The node keeps retrying and binds nothing until the panel sends a list without such a user, and then comes up by itself. Give every plan and every user a speed limit;0means unlimited. Speed limits explains this.
Send the request by hand
Section titled “Send the request by hand”katana’s panel error lines never contain the URL, because the URL carries the key. To see what the panel answers, send the same request with curl. Reading the key into a variable keeps it out of your shell history:
read -rs KEY # paste the panel key, then press Entercurl -sS -i "https://panel.example.com/api/v1/server/UniProxy/config?node_id=1&node_type=v2ray&token=$KEY"Use the node_type katana sends: your node_type in lower case, or vless when a V2ray, VMess or VLESS node has enable_vless = true. A working node answers 200 with JSON that contains server_port. Xboard answers a wrong key with Invalid token, an unknown type with Invalid node type specified, and an ID that has no node of that type with Server does not exist. For the user list, replace config with user.
read -rs KEY # paste the muKey, then press Entercurl -sS -i "https://panel.example.com/mod_mu/nodes/1/info?key=$KEY&muKey=$KEY"A working node answers 200 with "ret":1. For the user list, request /mod_mu/users?key=$KEY&muKey=$KEY&node_id=1.
Run it from the node’s host, so that DNS, routing and firewalls are the same as katana’s. A connection error, a certificate error, an HTTP error status or a body that is not JSON each point to a different fix, and the panel’s error message usually names the problem.
After you fix the cause
Section titled “After you fix the cause”You do not need to restart katana. The node picks up the fix at its next attempt, at most 60 seconds later, or one update_periodic interval if that is shorter. If the fix is in the node’s [[node]] table, saving the file ends the wait and starts the next attempt at once, with the new settings. Check the file with katana --test before you save.
A node that was up goes dark
Section titled “A node that was up goes dark”A node that has come up once recovers from most problems on its own:
| Log line | What happened | What happens next |
|---|---|---|
node 1: rebuild failed: … |
A listener rebuild, after a panel or file change, did not build or bind. The message is one of the initial start failed causes above. |
The node has no listener. Every poll tries again, so it comes back at the first poll after you fix the cause. |
| none, and the port closes | The panel’s user list for the node became empty. | The node binds again at the first poll that returns users. |
node 1: refreshed port is 0, keeping the last one |
The panel reported port 0 at a poll. |
The node keeps its current listener and port. It still applies the user list from that poll. |
hysteria listener failed: … |
A Hysteria 2 node’s QUIC listener stopped. | Polls do not rebuild it. Restart katana, or save an edit that rebuilds the node’s listener, such as a change in [node.hysteria]. |
A config edit had no effect
Section titled “A config edit had no effect”katana reloads its config file about 500 ms after you save it, and applies almost every change at once. An edit to [node.api], such as speed_limit, timeout or rule_list_path, gives the node a new panel client, which reads the node and its users again at once; connections drop only if that changes the node’s protocol or transport. enable_vless, and on Xboard and V2board a node_type change, can drop connections as well; see the first two rows below. The exceptions are a few files that katana reads only when it builds something, and saves that do not build, which katana refuses. A save that katana applies writes a reload: line for each part it changed, and a refused one writes a line that says keeping current or refused.
| You changed | What happened | What to do |
|---|---|---|
api.node_type or api.enable_vless on Xboard or V2board, so that katana asks the panel for another type, and the node restarted |
The panel finds a node by its ID and the type katana asks for, so the new type names another panel node. katana stops the old node, with a final traffic report, and starts one for the new type: reload: removing node …#1/v2ray, then reload: added node …#1/vless. Every connection on the node drops, and the new node starts with empty traffic counters. |
Expected. Make the change when the node is quiet. |
disable_sniffing, a key in [node.hysteria], or api.enable_vless on SSPanel (on Xboard or V2board, when it does not change the type katana asks for), and the node’s connections dropped |
These apply at once by rebuilding the node’s listener, which ends every connection on it. | Expected. Make these edits when the node is quiet. |
controller.send_ip, api.device_limit |
katana accepts these keys and does not use them. | Nothing to apply. |
[dns] alone |
The resolver is rebuilt only together with the outbounds, and katana logs no reload: line. |
Restart katana, or apply it with your next [[outbound]] edit. |
| A renewed certificate, same paths | katana reads the certificate files when it builds a listener. | Restart katana after each renewal. |
Replaced geoip or geosite files, same paths |
katana reads them when it compiles a node’s routes. | Restart katana, or save a change to [node.route] or [[outbound]]. |
[node.route], and the log says node 1: config edit refused, keeping the running one: … |
The node’s new routes do not compile. The node applies nothing from its edited [[node]] table and keeps its rules, its listener and its connections. The reload still logs reload: reconfigured node …, and the rest of the save, such as other nodes and the log level, still applies. |
Fix the rule and save again. Run katana --test before you save. |
[[outbound]], and the log says node 1: route rebuild failed, keeping current: … |
The node’s routes name an outbound that the new pool no longer has. The node keeps routing with its previous table, which still points at the old outbounds, and rebuilds its listener. | Restore the outbound, or change the node’s routes to match, and save again. |
[[outbound]], and the log says reload: bad outbounds, keeping current config: … |
The new outbounds do not build, so katana applied nothing from that save. | Fix the outbound and save again. |
Anything, and the log says reload: node …: …; keeping current config |
A [[node]] table in the new file does not build: an unknown panel_type or node_type, or routes that do not compile in a node the save adds (build router: …). katana applied nothing from that save. |
Fix the node the line names and save again. katana --test reports the same error. |
Anything, and the log says config reload failed, keeping current: … |
The file does not parse. | Fix the file and save again. |
| Anything, and no line appears at all | The file watcher did not start (config watcher disabled (no live reload): … at startup), or the config path is a symlink into another directory, whose changes katana does not see. |
Restart katana after each edit. |
| Anything in a node that has not come up yet | Nothing is lost: the node is still running and retrying. It takes the edit and starts its next attempt at once, with the new settings. | Read the node’s next line: listening on …, or retrying in <N>s with the cause that is left. |
The full table of what each key does on reload, and which changes drop connections, is in Hot reload.
Clients cannot connect
Section titled “Clients cannot connect”katana writes nothing at the default info level when a single connection fails. If the node’s port is open (see Where to look first), work through the stages a connection passes, in order:
| Stage | What can fail | Where to look |
|---|---|---|
| Reaching the port | A firewall, a cloud security group, UDP blocked for Hysteria 2, an IPv6 client with listen_ip = "0.0.0.0" |
Is the port reachable? |
| TLS | Missing or wrong certificate, a server name the certificate does not cover | Certificates |
| Transport | WebSocket path or Host, gRPC service name |
VLESS over WebSocket or gRPC |
| Protocol | A client protocol that differs from what katana serves, an unknown user, clock skew | node_type and the panel’s protocol, VMess and the clock |
| The destination | A route to block, an audit rule, a failing outbound |
An audit rule does not block, A WireGuard outbound passes no traffic |
To see the reason for each failed handshake, turn on katana::serve=debug as described in Raise the log detail. The lines look like this:
DEBUG katana::serve: inbound handshake failed: proxy core: vmess: unknown user or invalid auth idDEBUG katana::serve: inbound handshake failed: proxy core: invalid vless request user idDEBUG katana::serve: inbound handshake failed: proxy core: trojan: invalid userDEBUG katana::serve: inbound handshake failed: timed out after 10sDEBUG katana::serve: inbound handshake failed: closed before the handshake completedDEBUG katana::serve: inbound transport ended: …inbound transport ended covers failures below the proxy protocol, such as a TLS handshake or a WebSocket upgrade that did not complete. A client that is still in its protocol handshake after 10 seconds is disconnected with timed out after 10s. These lines do not name the node or the user.
Is the port reachable?
Section titled “Is the port reachable?”- Firewall. Open the port for TCP on VMess, VLESS, Trojan and Shadowsocks nodes, and for UDP on Hysteria 2 nodes, both in the host firewall and in any cloud security group. Hysteria 2 fails silently when UDP is blocked: the client times out and katana logs nothing. For example,
ufw allow 443/udporfirewall-cmd --add-port=443/udp --permanent. - Address family. The default
listen_ip = "0.0.0.0"accepts IPv4 only. To accept IPv6 clients as well on Linux, setlisten_ip = "::", which also accepts IPv4 unless the host setsnet.ipv6.bindv6only = 1. - File descriptors.
accept error, backing off 100ms: Too many open files (os error 24)means katana has reached its open-file limit, so new clients wait or fail. Raise the limit, for example withLimitNOFILE=in the systemd unit.
Certificates
Section titled “Certificates”katana takes every certificate from [node.controller.cert]. It ignores the TLS settings and certificates a panel sends, so a certificate uploaded to the panel is never served.
- A missing file keeps the node from coming up: it retries with
initial start failed: No such file or directory (os error 2)until the file is there. See Start-up log lines. - Clients check the certificate’s Subject Alternative Names against the server name they send. A certificate that names the host only in its Common Name, or that covers a different name, fails on the client with a certificate error, and katana sees only a failed TLS handshake.
- A self-signed certificate works only when the client pins it or skips verification.
- katana keeps serving the certificate it read when it built the listener. After a renewal, restart katana; otherwise clients see the old certificate expire.
node_type and the panel’s protocol
Section titled “node_type and the panel’s protocol”node_type decides which protocol katana serves, whatever the panel says the node is. When the two disagree, either the panel refuses the request or katana serves a protocol the clients do not speak.
With Xboard or V2board, katana sends a node_type with every request, and the panel looks the node up by ID and type together:
| Node in the panel | node_type |
enable_vless |
katana sends |
|---|---|---|---|
| VMess | V2ray or VMess |
false |
v2ray or vmess |
| VLESS | V2ray |
true |
vless |
| Trojan | Trojan |
false |
trojan |
| Shadowsocks | Shadowsocks |
false |
shadowsocks |
| Hysteria 2 | Hysteria2 or Hysteria |
false |
hysteria2 or hysteria |
The common mistakes:
node_type = "vless"withoutenable_vless = true. katana asks the panel for the VLESS node, receives it, and serves VMess on it. Every client fails withvmess: unknown user or invalid auth id. Setenable_vless = trueand save. The node keeps its panel node, because katana already asks forvless, and rebuilds its listener at once to serve VLESS.node_type = "hy2". katana accepts the name, but it sends it to the panel unchanged and Xboard does not know it, so every request to the panel fails withInvalid node type specified. UseHysteria2.- A type the node does not have under that ID. The panel finds no node, and the request fails with
node_info failed: GET /api/v1/server/UniProxy/config. Sending the request by hand shows Xboard’sServer does not exist. - Trojan over WebSocket or gRPC. For Trojan nodes from Xboard or V2board, katana ignores the panel’s
networkand always serves Trojan over TCP with TLS. Configure Trojan clients for TCP.
With SSPanel, the panel finds a node by its ID alone and katana reads it as the node_type you configured. A mismatch starts the node, but clients fail their handshakes. Make node_type match the node’s type in SSPanel.
VMess and the clock
Section titled “VMess and the clock”VMess authentication includes a timestamp, and katana accepts it only within 120 seconds of its own clock in either direction. Outside that window every connection fails with vmess: unknown user or invalid auth id, the same line as for an unknown user. Shadowsocks nodes with a 2022-blake3-… cipher are stricter: the request header’s timestamp must be within 30 seconds, and a client outside that window fails with shadowsocks-2022: bad timestamp. Check that both the server and the client keep time:
timedatectl status # "System clock synchronized: yes"If one client fails while others on the same node work, check that device’s clock and time zone setting. VLESS, Trojan, Hysteria 2 and the older Shadowsocks ciphers do not depend on the clock this way.
VLESS over WebSocket or gRPC on current Xboard
Section titled “VLESS over WebSocket or gRPC on current Xboard”katana reads a VLESS node’s transport settings from the network_settings key of the panel’s answer. Current Xboard releases send them as networkSettings for every node type, so katana sees no path, Host or service name:
- a VLESS WebSocket node listens on path
/and accepts anyHost, so clients configured with another path get HTTP404; - a VLESS gRPC node expects an empty service name, so clients configured with one fail.
To confirm, send the config request by hand and look for networkSettings without network_settings. The ways around it are to set the WebSocket path to / in the panel, to serve VLESS over TCP with TLS, or to serve the WebSocket or gRPC node as VMess. VMess nodes read networkSettings and are not affected.
Hysteria 2 clients
Section titled “Hysteria 2 clients”Beyond the UDP firewall:
- Credential. By default the client’s password is the user’s UUID. With
[node.hysteria].credential = "user_pass", it is<uuid>@v2board.user:<uuid>on Xboard and V2board, and<user id>:<uuid>on SSPanel. A client that sends the wrong form getsauthentication error, HTTP status code: 404, or your masquerade status. - Obfuscation. The client’s
salamanderpassword must match the node’s: the panel’sobfs-password, or[node.hysteria].obfs_passwordfor a node described locally. A mismatch looks exactly like blocked UDP, a timeout with no authentication error, because katana cannot read the packets. - Certificate. Hysteria 2 clients verify the certificate like any TLS client; see Certificates.
RUST_LOG=info,etemenanki_protocols::hysteria=debug shows the listener’s view, for example hysteria2: a handshake failed: …. Hysteria 2 nodes covers the settings.
- A new user cannot connect yet. katana learns about user changes at its next poll, up to
update_periodicseconds (default 60) after the change. - One user is never accepted. On a
V2raynode, a user whose UUID is not a valid UUID is left out, withskipping user 7: uuid is not a valid UUIDatWARN. Fix the UUID in the panel. - No user added since some time ago can connect. Look for
user_list: …warnings at every poll: katana keeps serving the last user list it could read. On Xboard,user_list: parse UniProxy user responseis usually a user or plan without a speed limit; see A node with no users. - A removed user keeps working. Removal takes effect at the next poll, and then ends that user’s open connections as well.
Speeds are not what you expect
Section titled “Speeds are not what you expect”A user’s rate is the smaller of the node’s limit and the user’s own limit, ignoring any that are 0. Only SSPanel has a node limit; on Xboard and V2board the user’s limit is the rate. katana enforces it with one token bucket per user on the node. Speed limits explains the mechanism.
| Symptom | Cause | What to do |
|---|---|---|
| Every user on the node has the same speed, whatever their plan | [node.api].speed_limit is above 0. It replaces every user’s limit, including users whose plan has none. It is not a ceiling. |
Set it to 0 and save. The node’s users get their own limits at once, for the flows they open from then on, and keep their connections. |
| The rate is 8 times lower or higher than you meant | All limits are in megabits per second, decimal. speed_limit = 100 is 12,500,000 bytes per second, which a client shows as about 12.5 MB/s. |
Convert: bytes per second times 8, divided by 1,000,000. |
| A changed limit applies to some traffic but not all | A limit changed in the panel applies at the next poll, and a speed_limit saved in the file applies at once, Hysteria 2 nodes included. Either way the change is made in place, without dropping connections, and only flows opened after it use the new rate. Flows that were already open finish at the old rate. |
Wait for the client to open new connections. |
| A user downloading and uploading at once gets about half the rate each way | Both directions, and every connection of the user on the node, share one bucket. | Expected. Size the plan for combined traffic. |
| An SSPanel user is slower than their plan | The node’s own limit in SSPanel is lower, and the smaller one wins. | Raise or clear the node’s limit. |
| A Hysteria 2 client’s declared bandwidth makes no difference | katana’s per-user limit comes only from the panel and the config file. | Set the limit in the panel. |
| Everyone is slow, with no limits set | The bottleneck is elsewhere: the host, its link, or the outbound. | Test without katana, and see A WireGuard outbound passes no traffic for tunnels. |
Traffic does not show in the panel
Section titled “Traffic does not show in the panel”katana reports traffic at every poll, every update_periodic seconds. It sends nothing for users who moved no bytes, and sends no request at all when nobody did.
| Symptom | Cause | What to do |
|---|---|---|
| No traffic at all, and no warnings | [node.controller].disable_upload_traffic = true |
Set it to false; the change applies at the next report. Traffic reporting explains what happens to the counts meanwhile. |
| No traffic yet, right after start | The first report comes one full update_periodic interval after the node came up. |
Wait one interval. |
node 1: report traffic: POST UniProxy push |
A report to Xboard or V2board failed. | See Panel request errors. katana keeps the counts and adds them to the next report. |
node 1: report traffic: POST /mod_mu/users/traffic, or … panel returned ret=0 |
A report to SSPanel failed or was refused. | The same. |
| The node has no listener | It has not come up yet and is retrying, so it has nothing to report. | See A node never comes up. |
| Xboard shows the node online but without a push | No user moved any bytes for a while, so katana sent no push. | Expected on an idle node. |
| The panel shows less than the client or the network interface | katana bills payload only: TLS, WebSocket, gRPC and proxy protocol overhead are not counted. | Expected. |
| The panel shows more than katana carried | Xboard multiplies by the node’s rate, or a report timed out on katana’s side after the panel had accepted it, and katana sent it again. | Check the node rate. See Traffic reporting, and raise api.timeout if the panel is slow. |
304 Not Modified answers in the panel’s access log are normal. katana asks for the node config and the user list, and on SSPanel the audit rules, with the tag of the last answer. 304 means nothing changed, and katana keeps what it has without logging anything. Traffic reports are POST requests and never get a 304.
An audit rule does not block
Section titled “An audit rule does not block”katana tests audit rules against the host in the client’s request, before it dials. Destination audit explains the matching in full. The usual reasons a rule does not fire:
- The client connects by IP address. The rule sees only the address in the proxy request, never a name sniffed from the TLS server name or HTTP
Host. A domain rule does not match a client that resolved the name itself. To block a site however the client addresses it over TCP, add adomain_suffixrule that sends it toblockin routing, which does see the sniffed name while sniffing is on. - Case and anchors. Matching is case-sensitive: start the pattern with
(?i).^blocked\.example\.com$does not matchwww.blocked.example.com. - Rules are off. With
disable_get_rule = trueat start, katana loads no audit rule, the local file included. Setting it totrueby a reload stops the refresh but keeps the rules already loaded; setting it back tofalseloads them at the next poll. - The rule has not loaded yet, or failed to. Rules load right after the node starts and refresh at every poll. Look for
invalid local rule …,invalid block rule …,invalid panel rule …orcannot read rule_list_path …atWARN, and on SSPanel fornode_rule: …warnings. - A route already blocks it. A flow that routing sends to
blockis refused before the audit rules run, so it records no audit hit. - The flow was already open. Rules apply to new flows. Open TCP connections are not checked again.
- A local rule file edit on SSPanel. When SSPanel answers the rules request with
304, katana does not re-read the local file either. The edit applies when the panel’s rules change, when a saved edit to the node’s[node.api]gives it a new panel client, or when katana restarts.
A refused flow looks the same to the client as a route to block, and katana writes no log line for it. On SSPanel, hits from panel rules reach the detection log at the next poll. Hits from local rules, and every hit on Xboard or V2board, are not reported anywhere.
A WireGuard outbound passes no traffic
Section titled “A WireGuard outbound passes no traffic”When the destinations routed to a wireguard outbound time out while everything else works, the outbound is the problem. katana writes no line of its own for a connection that times out through the tunnel. At the default level you may see these warnings instead:
| Warning | Meaning |
|---|---|
HANDSHAKE(REKEY_TIMEOUT) (target boringtun) |
The peer does not answer handshakes. After about 90 seconds of this, CONNECTION_EXPIRED(REKEY_ATTEMPT_TIME) follows at ERROR. |
wireguard: tunnel start failed: … |
The tunnel cannot start at all, for example because its endpoint does not resolve. |
wireguard: tunnel driver stopping, send failed: … or … receive failed: … |
The tunnel’s UDP socket failed, and the tunnel stops. |
wireguard: tunnel driver stopped, rebuilding |
The next connection found the tunnel stopped, and katana starts a new one. |
A tunnel whose handshake completes logs nothing at info, whether or not traffic passes. Work through these steps:
-
Test the upstream outside katana. Run the same tunnel in a throwaway etemenanki-app with a loopback SOCKS inbound, and fetch an IP echo service through it. Outbounds has the procedure and a ready config. It touches nothing in the running katana.
-
Retry the first request once. The first connection through a new tunnel waits for the WireGuard handshake, and with some services the very first connection with a newly issued key fails. Retry once before you decide the key is bad: a first request that times out and a second that works mean the configuration is fine.
-
Do not trust the handshake alone. A completed handshake shows that the peer knows your public key. It does not show that the service still forwards traffic for it: some services complete the handshake for a key they no longer accept and then drop every packet. Only a request that returns the upstream’s exit address proves the key works. If the handshake completes but nothing passes, get a new key or configuration from the service first.
-
Read the tunnel’s own log. Turn on the WireGuard targets in katana, or in the throwaway etemenanki-app:
Terminal window RUST_LOG=info,boringtun=debug,etemenanki_protocols::wireguard=debugSending handshake_initiationrepeated with noReceived handshake_responsemeans the peer does not answer: checkserver,port,public_key,pre_shared_keyandreserved, and that outbound UDP is allowed.Received handshake_responseandNew sessionfollowed by timeouts means the tunnel is up and the peer is not forwarding. WireGuard explains these lines in full. -
If small requests work and large ones stall, lower
mtu, for example to1280.
Panel request errors
Section titled “Panel request errors”A panel error line names the request that failed, not the reason:
WARN katana::manager::node: node 1: node_info: GET /api/v1/server/UniProxy/configkatana writes only the outermost description of the error: the method and the path. It leaves out the panel’s host name, the query string with the key, and the underlying cause, such as a refused connection, a timeout or the HTTP status. The same line therefore covers a wrong host, an unreachable panel, a request slower than api.timeout, and every 4xx or 5xx answer. Send the request by hand to see which.
Line, after node <node_id>: |
Request |
|---|---|
node_info failed: …; retrying in <N>s (while the node comes up, ERROR) or node_info: … (at a poll, WARN) |
The node’s settings |
user_list failed: …; retrying in <N>s (while the node comes up, ERROR) or user_list: … (at a poll, WARN) |
The user list |
report traffic: … |
The traffic report |
node_rule: … |
The SSPanel audit rules |
report illegal: … |
The SSPanel detection log |
The part after the colon tells you how far the request got:
| Ending | Meaning |
|---|---|
GET /api/v1/server/UniProxy/config, GET /mod_mu/users, POST UniProxy push, POST /mod_mu/users/traffic |
No usable answer: a network or TLS error, a timeout, or an HTTP error status. |
GET /mod_mu/users body and similar, ending in body |
The answer broke off while katana was reading it. |
parse UniProxy config response, parse UniProxy user response, parse /mod_mu/users, parse sspanel user list and similar |
The panel answered, but not with the JSON katana expects: often an HTML error page, panel_type set for the other panel, or on Xboard a user with no speed limit. |
/mod_mu/users: panel returned ret=0 and similar |
SSPanel answered and refused the request. |
What each failure costs:
- While a node comes up, a failed settings or user request makes it wait and try again: see A node never comes up.
- At a poll, katana keeps the last settings and users it received. Nothing is torn down, and the next poll tries again.
- A failed traffic report keeps its counts for the next report, so nothing is lost.
- A failed detection-log report is dropped.
api.timeout is the limit for each request, in seconds; 0 or unset means 5. A panel that is often slower needs a larger value. A saved change applies at once: katana rebuilds the node’s panel client in place and keeps the node’s connections.
Handshake failure warnings
Section titled “Handshake failure warnings”WARN katana::manager::proxy: node V2ray_0.0.0.0_443: 37 inbound handshake failures in the last 1s (possible handshake scan/DoS or misconfigured clients)Each TCP-based node counts the connections that fail before a client is authenticated, and checks the count every second. When a second has more than 10, katana writes this warning. It counts:
- transport handshakes that fail before the first stream: TLS, the WebSocket upgrade, the HTTP/2 preface of gRPC;
- protocol handshakes that fail: an unknown user, a malformed request, a client that closes early or does not finish within 10 seconds;
- connections refused because the node reached its live-connection or pre-authentication limit.
The warning is an alert only: katana blocks nothing because of it. Hysteria 2 nodes do not count failures and never write it.
What it usually means:
| Pattern | Likely cause | What to do |
|---|---|---|
| A burst right after you changed the node’s transport, TLS or protocol in the panel | Clients still use the old settings. | Nothing, or have users refresh their subscription. |
| A steady stream at all hours, from many addresses | Internet scanners probing the port. | Usually harmless. Rate-limit new connections in the host firewall if the noise matters. |
| Every client fails, the warning is constant, and nobody can connect | A mismatch that affects everyone: node_type, enable_vless, the certificate or the clock. |
See Clients cannot connect. |
The count is very high, and the debug log shows pre-auth limit reached or live connection limit reached |
The node is at its connection guardrails. | See Connection guardrails. |
To see what fails, turn on katana::serve=debug for a minute and read the inbound handshake failed and inbound transport ended lines, as shown in Clients cannot connect.