perspective-dev/perspective · error · std::runtime_error

Unhandled request type

Error message

Unhandled request type

What it means

The Perspective server's static helper `needs_poll()` classifies every incoming protobuf `Request` by its `ClientReqCase` oneof discriminator to decide whether polling is required. It throws "Unhandled request type" when the request's oneof case holds a value that is not listed in any branch of the switch — i.e. an enum value that exhausts neither the poll nor the no-poll list. This is a defensive fallthrough guard: it fires when a new request type was added to the proto without updating this dispatcher, or when the received enum value is unknown to this build.

Solutions

  1. Check that client and server use the same perspective version/proto so no unknown request types are sent.
  2. If you added a new request type to the proto, add the corresponding `case ReqCase::k...` to `needs_poll()` (and the other dispatchers like `entity_type_is_table`) in server.cpp.
  3. Log the raw request (`req.DebugString()` / numeric oneof case) before dispatch to identify which case value is slipping through.
  4. Rebuild both sides from the same source tree so generated proto enums match exactly.

Example fix

// before (server.cpp, needs_poll)
case ReqCase::kGetFeaturesReq:
case ReqCase::kMakeJoinTableReq:
    return false;
case proto::Request::CLIENT_REQ_NOT_SET:
    throw std::runtime_error("Unhandled request type 2");
}
throw std::runtime_error("Unhandled request type");

// after (handle the new request type explicitly)
case ReqCase::kGetFeaturesReq:
case ReqCase::kMakeJoinTableReq:
case ReqCase::kMyNewRequestReq:  // newly added proto case
    return false;
case proto::Request::CLIENT_REQ_NOT_SET:
    throw std::runtime_error("Unhandled request type 2");
}
Defensive patterns

Strategy: validation

Validate before calling

// Python client: verify the request oneof is populated before sending
# and that client/server proto versions match
assert req.WhichOneof("client_req") is not None, "empty Request"
# optionally check known-case coverage:
KNOWN_CASES = {"make_table_req", "table_size_req", "view_schema_req"}  # etc.
assert req.WhichOneof("client_req") in KNOWN_CASES, (
    f"server build may not know request type {req.WhichOneof('client_req')}")

Type guard

// TypeScript/JS client over perspective WASM/socket transport
function isKnownRequestType(req: { [k: string]: unknown }): boolean {
  const key = Object.keys(req).find((k) => k !== "reqId");
  return key !== undefined && key !== "clientReqNotSet";
}

Try / catch

// caller wraps the round-trip; server errors surface as a rejected/errored response
try {
  const resp = await client.send(request);
} catch (e) {
  if (String(e?.message).includes("Unhandled request type")) {
    // version skew: server predates this request type — fall back or upgrade server
  }
  throw e;
}

Prevention

When it happens

Trigger: Sending the server a `Request` protobuf whose oneof case is set to a value not covered by `needs_poll()`'s switch (e.g. a newly added request kind compiled into the client but not into this server binary), or a corrupted/misencoded oneof tag that decodes to an out-of-range case value. Note it is NOT thrown for `CLIENT_REQ_NOT_SET` (that path throws "Unhandled request type 2" instead).

Common situations: Version mismatch between client and server (client built from a newer perspective-proto than the server), a custom fork adding new request types to `proto::Request` without updating `server.cpp` dispatchers, or a buggy/malformed serialization pipeline producing garbage oneof tags.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/c666d99818f20db6. Report an issue: GitHub.

Appendix: source

Thrown at rust/perspective-server/cpp/perspective/src/cpp/server.cpp:1275

        case ReqCase::kTableUpdateReq:
        case ReqCase::kTableRemoveDeleteReq:
        case ReqCase::kGetHostedTablesReq:
        case ReqCase::kRemoveHostedTablesUpdateReq:
        case ReqCase::kTableReplaceReq:
        case ReqCase::kTableDeleteReq:
        case ReqCase::kViewGetConfigReq:
        case ReqCase::kViewColumnPathsReq:
        case ReqCase::kViewDeleteReq:
        case ReqCase::kViewExpressionSchemaReq:
        case ReqCase::kViewRemoveOnUpdateReq:
        case ReqCase::kServerSystemInfoReq:
        case ReqCase::kGetFeaturesReq:
        case ReqCase::kMakeJoinTableReq:
            return false;
        case proto::Request::CLIENT_REQ_NOT_SET:
            throw std::runtime_error("Unhandled request type 2");
    }
    throw std::runtime_error("Unhandled request type");
}

static constexpr bool
entity_type_is_table(const proto::Request::ClientReqCase proto_case) {
    using ReqCase = proto::Request::ClientReqCase;

    switch (proto_case) {
        case ReqCase::kTableSizeReq:
        case ReqCase::kTableSchemaReq:
        case ReqCase::kTableMakePortReq:
        case ReqCase::kTableValidateExprReq:
        case ReqCase::kMakeTableReq:
        case ReqCase::kTableOnDeleteReq:
        case ReqCase::kTableRemoveReq:
        case ReqCase::kTableUpdateReq:
        case ReqCase::kTableRemoveDeleteReq:
        case ReqCase::kGetHostedTablesReq:
        case ReqCase::kServerSystemInfoReq:

View on GitHub (pinned to 11c8238c0c)