{"record":{"id":"d24bee19653ce7bc","repo":"fatedier/frp","slug":"unexpected-frame-type-d-want-d-d24bee","errorCode":null,"errorMessage":"unexpected frame type %d, want %d","messagePattern":"unexpected frame type (.+?), want (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/proto/wire/wire.go","lineNumber":115,"sourceCode":"\n\theader := make([]byte, 8)\n\tbinary.BigEndian.PutUint16(header[0:2], f.Type)\n\tbinary.BigEndian.PutUint16(header[2:4], f.Flags)\n\tbinary.BigEndian.PutUint32(header[4:8], uint32(len(f.Payload)))\n\tif _, err := c.rw.Write(header); err != nil {\n\t\treturn err\n\t}\n\t_, err := c.rw.Write(f.Payload)\n\treturn err\n}\n\nfunc (c *Conn) ReadJSONFrame(frameType uint16, out any) error {\n\tf, err := c.ReadFrame()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif f.Type != frameType {\n\t\treturn fmt.Errorf(\"unexpected frame type %d, want %d\", f.Type, frameType)\n\t}\n\treturn c.UnmarshalFrame(f, out)\n}\n\nfunc (c *Conn) UnmarshalFrame(f *Frame, out any) error {\n\treturn json.Unmarshal(f.Payload, out)\n}\n\nfunc NewJSONFrame(frameType uint16, in any) (*Frame, error) {\n\tpayload, err := json.Marshal(in)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Frame{\n\t\tType:    frameType,\n\t\tPayload: payload,\n\t}, nil\n}","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/proto/wire/wire.go#L97-L133","documentation":"Returned by Conn.ReadJSONFrame when the frame just read has a Type different from the expected frameType argument. ReadJSONFrame is a convenience that reads one frame and asserts its type, so an out-of-order or unexpected message produces this error rather than a silent misparse.","triggerScenarios":"Calling ReadJSONFrame(FrameTypeServerHello, ...) but the server sent an error frame first; protocol state machine getting out of order (two readers, or reading after an error); peer sending a frame type from a newer protocol.","commonSituations":"Server replies with an error/close frame where the client expects the next handshake frame; concurrent goroutines reading from the same Conn; version skew introducing new frame types.","solutions":["Read frames generically with ReadFrame and dispatch on f.Type, handling error frames, instead of hard-expecting one type.","Ensure exactly one goroutine owns the read side of the connection.","After any prior read/write error, treat the connection as dead — do not keep expecting the next typed frame."],"exampleFix":"// before\nvar hello wire.ServerHello\nconn.ReadJSONFrame(wire.FrameTypeServerHello, &hello) // fails if server sent error frame\n\n// after\nf, err := conn.ReadFrame()\nif err != nil { return err }\nswitch f.Type {\ncase wire.FrameTypeServerHello:\n    return conn.UnmarshalFrame(f, &hello)\ncase wire.FrameTypeError:\n    return parseAndReturnError(f)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"f, err := conn.ReadFrame()\nif err != nil { return err }\nif f.Type != expectedType {\n    if f.Type == frameTypeError {\n        return decodeRemoteError(f) // server sent an error frame instead\n    }\n    return fmt.Errorf(\"unexpected frame type %d, want %d\", f.Type, expectedType)\n}","preventionTips":["Prefer ReadFrame + explicit dispatch over ReadJSONFrame in nontrivial state machines.","Keep one reader goroutine per connection to preserve frame ordering."],"tags":["protocol","framing","state-machine","go"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}