{"record":{"id":"81a85a8cda6a4b22","repo":"MHSanaei/3x-ui","slug":"decode-envelope-w","errorCode":null,"errorMessage":"decode envelope: %w","messagePattern":"decode envelope: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/web/runtime/remote.go","lineNumber":286,"sourceCode":"\n\t// Fast-fail on an honestly-declared oversize body; the LimitReader below is\n\t// the real guard since Content-Length is untrusted, may be absent, or is -1\n\t// under transparent decompression.\n\tif resp.ContentLength > maxRemoteResponseBytes {\n\t\treturn nil, fmt.Errorf(\"%s %s: %w (content-length %d, cap %d)\", method, path, errRemoteResponseTooLarge, resp.ContentLength, maxRemoteResponseBytes)\n\t}\n\n\traw, err := readCappedBody(resp.Body, maxRemoteResponseBytes)\n\tif err != nil {\n\t\tif errors.Is(err, errRemoteResponseTooLarge) {\n\t\t\treturn nil, fmt.Errorf(\"%s %s: %w (cap %d bytes)\", method, path, err, maxRemoteResponseBytes)\n\t\t}\n\t\treturn nil, fmt.Errorf(\"read body: %w\", err)\n\t}\n\n\tvar env envelope\n\tif err := json.Unmarshal(raw, &env); err != nil {\n\t\treturn nil, fmt.Errorf(\"decode envelope: %w\", err)\n\t}\n\tif !env.Success {\n\t\treturn &env, &remoteAPIError{msg: env.Msg}\n\t}\n\treturn &env, nil\n}\n\nfunc (r *Remote) resolveRemoteID(ctx context.Context, tag string) (int, error) {\n\tif id, ok := r.cacheGetTag(tag); ok {\n\t\treturn id, nil\n\t}\n\tif err := r.refreshRemoteIDs(ctx); err != nil {\n\t\treturn 0, err\n\t}\n\tif id, ok := r.cacheGetTag(tag); ok {\n\t\treturn id, nil\n\t}\n\treturn 0, fmt.Errorf(\"remote inbound with tag %q not found on node %s\", tag, r.node.Name)","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/MHSanaei/3x-ui/blob/ad32144c42455696ea9f14e12168beac3e25f5d2/internal/web/runtime/remote.go#L268-L304","documentation":"Returned by Remote.do when the 200 response body is not valid JSON for the wire envelope ({success, msg, obj}). The node answered OK at the HTTP layer but the payload is not the expected protocol — most commonly an HTML error/login page, a plaintext proxy message, or truncated output. It indicates protocol mismatch, not a node-side business error.","triggerScenarios":"The master's request hit the node's web LOGIN page (session middleware redirected to /login and returned 200 HTML) because the RPC auth header/token was missing or rejected; a reverse proxy served a 200 status page; the body was truncated by an intermediary; node version speaks a different envelope.","commonSituations":"Node API token expired/rotated so auth middleware silently redirects; proxy rewrites the path and serves an SPA index.html with 200; master pointed at the node's subscription port instead of the panel port.","solutions":["Manually reproduce: curl -H 'Authorization: ...' https://node/panel/api/inbounds/list and look at the raw body — if it is HTML, the request never reached the API.","Re-save the node's credential on the master so the auth header the runtime sends is accepted by the node's middleware.","Fix the node address to point at the panel port, not the subscription/other listener.","Align master and node versions if the envelope shape changed between releases."],"exampleFix":"// before: wrong port (subscription server) configured as node address\n// err: decode envelope: invalid character '<' looking for beginning of value\n\n// after: point the node record at the panel's HTTPS port serving /panel/api/*","handlingStrategy":"validation","validationCode":"// Smoke-test the wire contract once per node at registration\nenv, err := r.do(ctx, http.MethodGet, \"panel/api/inbounds/list\", nil)\nif err != nil { return err } // a decode-envelope failure here means auth/path is wrong","typeGuard":"func isEnvelopeDecodeError(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"decode envelope:\")\n}","tryCatchPattern":"if err := r.do(ctx, method, path, body); err != nil {\n    if isEnvelopeDecodeError(err) {\n        return fmt.Errorf(\"node %s speaks wrong protocol (auth redirect or wrong port): %w\", r.node.Name, err)\n    }\n    return err\n}","preventionTips":["After registering a node, run one probe RPC from the master before relying on it.","Ensure the node API token is accepted — an auth redirect that returns 200 HTML is the classic cause.","Never front the node API with an SPA-serving proxy that masks errors as 200 HTML."],"tags":["json","protocol","auth","proxy","remote"],"backgroundTag":null,"analyzedSha":"ad32144c42455696ea9f14e12168beac3e25f5d2","analyzedAt":"2026-08-15T11:13:23.905Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}