Use this guide if v2rayN reports that the core failed to start, v2rayNG stops immediately after launch, or a custom config won’t load. First distinguish a config loading failure from a runtime connection issue. Then use the specific log message to check JSON, listening ports, protocols, and routing fields. Fix one issue at a time, then verify with a config test and a real request.
First, pinpoint where startup fails
A page failing to load doesn’t mean the core failed to start. The client interface can open even when its background core isn’t running. At startup, the core must read the config, build inbound and outbound handlers, and start listening locally. Only then can app traffic reach the routing rules.
- Exits immediately on startup: Look for messages such as
Failed to start,failed to load, orfailed to listen. Check the local config and listening setup. - Request logs appear after startup: The core has likely entered its running state. For remote timeouts, certificate errors, or refused connections, check the server address, transport settings, and network connectivity.
- No logs at all: Make sure you’re viewing core logs, not just subscription update logs. Check that the client actually launched the core executable and that the log destination is writable.
v2rayN is a desktop client, and the available config fields depend on the core in use. v2rayNG uses Xray, while v2flyNG uses V2Fly. Their configs overlap, but that doesn’t mean every extension field is interchangeable. Include both the client and core versions in troubleshooting notes—“latest” isn’t specific enough.
Capture a complete startup log
Stop repeatedly clicking Connect, back up the current config, then clear the log view or note the time. Start the core once and save everything from the core version line through the exit message. In v2rayNG, open Logs from the sidebar. In v2rayN, check the information output area in the main window. The exact location and layout vary by version, so look for the core startup messages.
| loglevel | Primary use | What to look for |
|---|---|---|
error | Capture errors | Keep the cause of load or connection failures |
warning | Review warnings and errors | Check whether a warning is related to this exit |
info | Add runtime context | Startup sequence and request handling context |
debug | Investigate in more detail | More detailed clues about internal processing |
none | Disable this log level | Avoid during troubleshooting |
For custom JSON, temporarily set log.loglevel to debug. This only increases log detail; it won’t fix the error. If the JSON can’t be parsed at all, the setting may never take effect. Check the process output or the standard error captured by the client instead.
{
"log": {
"loglevel": "debug"
}
}
Timestamps and connection IDs help correlate entries from the same operation. Errors containing multiple > symbols are usually wrapped error chains. Find the first failure related to the current startup, then read through to the specific cause at the end of the chain. Don’t stop at the outermost Failed to start, and don’t mix in a timeout from an earlier request.
JSON syntax errors: fix the structure before checking fields
When JSON parsing fails, protocols, DNS, and routing usually haven’t been built yet. Common causes include a trailing comma, a missing double quote, curly quotes introduced while copying, or a number written as a string. The reported position may come after the actual problem: the parser may not detect the incomplete structure until it reads the next character.
Error: invalid character '}' looking for beginning of object key string
Cause: The parser expected a field name before the object ended. This often happens when there’s a trailing comma after the last field.
Fix: Check the line before the closing brace, remove the trailing comma, and make sure object keys use standard double quotes.
Error: unexpected EOF
Cause: The input ended unexpectedly. A closing brace, bracket, or quote may be missing, or the file may have been truncated while saving.
Fix: Starting at your last edit, check that all brackets and quotes are paired. If the file is clearly incomplete, restore a backup before reapplying your changes one section at a time.
Error: cannot unmarshal string into Go struct field
Cause: The field’s data type doesn’t match what the core expects. Later log entries usually identify the field path and expected type.
Fix: Check the type at the full field path. For example, a local inbound port should be a number: "port": 10808, not a quoted numeric string.
These are common error snippets; the full prefix, capitalization, and field path vary by core version. Don’t fix the issue with a global search and replace: a port field needs a number, but that doesn’t mean every value containing digits should lose its quotes. UUIDs, addresses, and tags are still strings.
- Open the file the core actually reads in a local editor with JSON validation.
- Fix the first syntax error reported by the editor, validate again, then move on to the next one.
- Standard JSON doesn’t allow trailing commas or comments. Even if a loader accepts extensions, stick to standard JSON while troubleshooting.
- If the client generates the config from form settings, make the change in the relevant setting instead. Direct edits to a temporary generated file may be overwritten on the next startup.
Port already in use: identify the process instead of blindly running as admin
Inbound creation commonly fails when two clients try to listen on the same address and port, or when a process from a previous launch is still running. The ports below are examples, not defaults for every client. Get the actual port from the log or current config, then replace the numbers in the commands.
Error: address already in use
Cause: The requested listening address and port are already in use or conflict with an existing listener.
Fix: Identify the process listening on the port and close any duplicate instance normally. If you need to run multiple instances, assign each a different inbound port and update the apps’ proxy settings accordingly.
Error: Only one usage of each socket address
Cause: A Windows socket binding conflict. The full error usually includes the local address and port.
Fix: Find the listening PID for the port shown in the log. Running as administrator is not a general fix for port conflicts.
Windows: check the listening PID
Get-NetTCPConnection -LocalPort 10808 -State Listen |
Select-Object LocalAddress, LocalPort, OwningProcess
Run this in PowerShell, then match the PID against the Details tab in Task Manager. Confirm which app owns it before closing anything; don’t terminate unknown system processes based on port number alone. If nothing is listening, check the full log for a permissions or unavailable-address error instead.
Linux and macOS: find the process listening on the port
# Linux
ss -ltnp 'sport = :10808'
# macOS
lsof -nP -iTCP:10808 -sTCP:LISTEN
A regular user may not be able to see full details for processes owned by other users. If needed, elevate permissions only for the inspection command; don’t run the entire client with elevated privileges long-term. If you change the local port from 10808 to 10810, update your browser, system proxy, and any apps configured manually as well.
Protocol and field errors: confirm which core is in use
Valid JSON only means the file can be parsed; it doesn’t mean the core understands its protocols. Spell protocol values exactly, such as vmess, vless, socks, and freedom. Inbound and outbound settings have different structures. Don’t paste server-side user settings into a client outbound.
- Check the version first: Note the Xray or V2Fly name and version in the startup log. The client version and core version are separate pieces of information.
- Then check the direction: Outbounds connect to remote servers; inbounds listen locally. Even for the same protocol, the fields on each side may differ.
- Finally, check extensions: Transports, security layers, and protocol extensions each have their own support requirements. Before moving a config to a different core, verify every field instead of simply changing the program name.
“Unknown protocol” may not appear as a single, consistent error line; it can be wrapped in an inbound or outbound load failure. Follow the error chain to the specific protocol value and compare it with the config. If an unfamiliar field doesn’t trigger an error, that doesn’t prove it took effect: some unknown JSON fields may be ignored. Verify against the current core documentation and actual behavior.
Routing errors: check conditions and outbound tags
Routing rules send connections that have already reached the core to an outbound. Rules in routing.rules are matched in order; a broad rule near the top can shadow more specific rules below it. The core may start successfully with this kind of config, yet send all traffic direct or route a domain through the wrong outbound. Treat that separately from structural errors that prevent startup.
Error: this rule has no effective fields
Cause: The rule has no effective match conditions—for example, it contains only type and outboundTag.
Fix: Add explicit conditions such as domain, ip, port, or network, or remove the unused empty rule.
Here’s a complete, minimal config for isolated testing: it provides a SOCKS inbound on 127.0.0.1:10808, sends example.com and its subdomains to a blocking outbound, and sends all other traffic through the first direct outbound. It doesn’t include a remote proxy node and shouldn’t replace a production node config.
{
"log": {
"loglevel": "info"
},
"inbounds": [
{
"tag": "local-socks",
"listen": "127.0.0.1",
"port": 10808,
"protocol": "socks",
"settings": {
"auth": "noauth",
"udp": false
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom",
"settings": {}
},
{
"tag": "blocked",
"protocol": "blackhole",
"settings": {}
}
],
"routing": {
"rules": [
{
"type": "field",
"domain": ["domain:example.com"],
"outboundTag": "blocked"
}
]
}
}
- Compare
outboundTagwithoutbounds[].tagexactly, including capitalization. A missing tag may behave differently depending on the version and code path, so a successful startup alone doesn’t prove the rule is correct. - Domain and IP match conditions use arrays. Multiple conditions in a single rule usually all need to match; don’t assume that adding a condition means any one of them can match.
- If the error points directly to
geosite.datorgeoip.dat, check the resource directory used by the current core, file permissions, and whether the referenced rule category exists. - To isolate a resource-file issue, temporarily replace the resource category with the explicit domain rule above. Once verified, restore the original rules one at a time.
Verify the fix in two steps
First, check only whether the core accepts the config. To test with the Xray command line, run the command below from the config directory. Use the same version of the executable as the client. On Windows, replace xray at the start of the command with .\xray.exe.
xray run -test -config ./config.json
A successful config test doesn’t guarantee that a port can be bound or that a remote server is reachable. For the second step, start the client, make sure there are no new listening errors, then send a request from an app through the correct local proxy port. The example config deliberately blocks example.com, so don’t use it as a site that should load successfully.
JSON validation passes, but the core still won’t start. Why?
JSON validation checks syntax only. Read the core output to distinguish a protocol build failure from a resource-file read error or a port binding failure. Test the config first, then start the core; record the result of each step separately.
Edits keep reverting after reconnecting. What’s going on?
You may have edited a runtime config that the client generates automatically. Back it up, then change the source data in the node editor, routing settings, or custom config interface. Start the client again and check the newly generated file and the config path shown in the logs.
The core is running, but the logs keep showing timeouts. What should I do?
First confirm that the app’s traffic reaches the local inbound. Then check the remote address, port, TLS server name, and transport path. A runtime timeout to a remote server isn’t a JSON syntax error; there’s no need to keep deleting the entire config.
Will reinstalling the client fix a port conflict?
Reinstalling won’t release a port held by another process. Find the PID using the actual conflicting port, such as 10808, then close the duplicate instance or change the port and update your app’s proxy settings.
A reproducible troubleshooting report should include the time of the test, client and core versions, config path, first failure in the log, the single field changed, and the retest result. Change one variable at a time to avoid fixing a port issue while introducing a routing error. To understand the field structure, check the configuration reference section by section.