{"record":{"id":"cdfb6f9a03d9f1dc","repo":"perspective-dev/perspective","slug":"unhandled-request-type-2","errorCode":null,"errorMessage":"Unhandled request type 2","messagePattern":"Unhandled request type 2","errorType":"exception","errorClass":"std::runtime_error","httpStatus":null,"severity":"error","filePath":"rust/perspective-server/cpp/perspective/src/cpp/server.cpp","lineNumber":1324,"sourceCode":"        case ReqCase::kViewToCsvReq:\n        case ReqCase::kViewToNdjsonStringReq:\n        case ReqCase::kViewToRowsStringReq:\n        case ReqCase::kViewToArrowReq:\n        case ReqCase::kViewSchemaReq:\n        case ReqCase::kViewGetMinMaxReq:\n        case ReqCase::kViewOnUpdateReq:\n        case ReqCase::kViewCollapseReq:\n        case ReqCase::kViewExpandReq:\n        case ReqCase::kViewSetDepthReq:\n        case ReqCase::kViewGetConfigReq:\n        case ReqCase::kViewColumnPathsReq:\n        case ReqCase::kViewDeleteReq:\n        case ReqCase::kViewExpressionSchemaReq:\n        case ReqCase::kViewRemoveOnUpdateReq:\n        case ReqCase::kRemoveHostedTablesUpdateReq:\n            return false;\n        case proto::Request::CLIENT_REQ_NOT_SET:\n            throw std::runtime_error(\"Unhandled request type 2\");\n    }\n    throw std::runtime_error(\"Unhandled request type\");\n}\n\nvoid\nProtoServer::handle_process_table(\n    const Request& req,\n    std::vector<ProtoServerResp<ProtoServer::Response>>& proto_resp\n) {\n    if (!m_realtime_mode && needs_poll(req.client_req_case())) {\n        if (entity_type_is_table(req.client_req_case())) {\n            if (m_resources.is_table_dirty(req.entity_id())) {\n                auto table = m_resources.get_table(req.entity_id());\n                const auto& table_id = req.entity_id();\n                _process_table(table, table_id, proto_resp);\n            }\n        } else {\n            auto table_id = m_resources.get_table_id_for_view(req.entity_id());","sourceCodeStart":1306,"sourceCodeEnd":1342,"githubUrl":"https://github.com/perspective-dev/perspective/blob/11c8238c0c9c0b9e490b5a6a2dc277afad1e980a/rust/perspective-server/cpp/perspective/src/cpp/server.cpp#L1306-L1342","documentation":"Thrown by the static helper `entity_type_is_table()` in the Perspective C++ server when the incoming `proto::Request` has no oneof member set at all (`CLIENT_REQ_NOT_SET`). The dispatcher must know which entity (table vs view) the request targets, and an empty request carries no such information. It means the client sent a request message that was missing its required payload field — every valid request must set exactly one of the `Request` oneof members (e.g. `make_table_req`, `view_schema_req`).","triggerScenarios":"Constructing a `proto::Request` and sending it without setting any of the oneof fields (e.g. calling `Send` on a default-constructed Request), clearing the oneof before sending, or transmitting truncated/corrupted bytes so the server-side parse yields a Request with `client_req_case() == CLIENT_REQ_NOT_SET`.","commonSituations":"A client bug where the request payload was conditionally skipped (e.g. an uninitialized or empty message), a serialization truncation over an unstable network/socket, or hand-rolled protobuf encoding that omits the oneof tag. Also seen when forwarding an inner message instead of the outer `Request` envelope.","solutions":["On the client, verify the Request is fully populated before sending: check `request.WhichOneof('client_req')` (Python) / the oneof case accessor is not `CLIENT_REQ_NOT_SET` and refuse to send otherwise.","Inspect the serialized payload on the wire; if it is truncated, fix the framing/length-prefix logic in the transport layer.","Ensure you are sending the outer `Request` envelope, not an inner message like `MakeTableRequest`, directly.","If you control the server, reject empty requests earlier with a clearer error instead of throwing from the constexpr dispatcher."],"exampleFix":"// before (client, Python): sends an incomplete request\nreq = perspective_pb2.Request()\nreq.table_size_req.table_name = \"t\"\n# ...but if table_name is empty some code paths skip filling the oneof\nsend(req)\n\n// after: guard before sending\nreq = perspective_pb2.Request()\nreq.table_size_req.table_name = \"t\"\nif req.WhichOneof(\"client_req\") is None:\n    raise ValueError(\"Request has no payload set; refusing to send\")\nsend(req)","handlingStrategy":"validation","validationCode":"# Python: refuse to send a Request whose oneof is unset\npayload = req.WhichOneof(\"client_req\")\nif payload is None:\n    raise ValueError(\"proto::Request has no client_req oneof set; server will reject with 'Unhandled request type 2'\")\nif not req.HasField(payload):  # defensible double-check\n    raise ValueError(f\"oneof field {payload} present but empty\")","typeGuard":"// TypeScript: narrow an untyped request envelope before sending\nfunction hasRequestPayload(req: Record<string, unknown>): boolean {\n  const oneofKeys = [\n    \"makeTableReq\", \"tableSizeReq\", \"tableSchemaReq\", \"viewSchemaReq\",\n    \"viewDeleteReq\", \"tableRemoveReq\", /* ... full known-case list */\n  ];\n  return oneofKeys.some((k) => req[k] != null);\n}","tryCatchPattern":"try:\n    resp = stub.handle_request(req, timeout=30)\nexcept grpc.RpcError as e:\n    if \"Unhandled request type\" in e.details():\n        # request reached the server with no oneof set — fix construction, not retry\n        raise RequestConstructionError(req) from e\n    raise","preventionTips":["Always construct requests through a helper that requires the oneof payload as an argument, never send default-constructed Request objects.","Call WhichOneof / the case accessor as a precondition before every send.","Beware clearing oneof fields (ClearField on the oneof name) after construction; re-validate afterwards.","Use length-prefixed framing and checksums on custom transports to avoid truncated protobuf parses yielding empty messages."],"tags":["cpp","protobuf","server","grpc","missing-payload"],"backgroundTag":"invalid-enum-value","analyzedSha":"11c8238c0c9c0b9e490b5a6a2dc277afad1e980a","analyzedAt":"2026-09-09T00:35:19.377Z","contentChangedAt":"2026-09-09T00:35:19.377Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}