perspective-dev/perspective · error · PerspectiveViewNotFoundException

PerspectiveViewNotFoundException

Error message

PerspectiveViewNotFoundException

What it means

`ServerResources::get_table_id_for_view` looks up which table a given view ID belongs to under a read lock; if the view ID is absent from the `m_view_to_table` map it throws `PerspectiveViewNotFoundException`. This means the client referenced a view the server no longer (or never did) track — typically because the view was deleted after a table was removed or the ID is stale/invalid.

Solutions

  1. Keep the parent table alive as long as any of its views are in use; delete views before deleting tables
  2. Wrap view operations in error handling that recreates the view (`table.view(...)`) when the server reports a missing view
  3. Avoid reusing view handles across server restarts/reconnects — recreate table and view after reconnecting
  4. Check for delete/race logic in your app that removes views or tables while other async work still references them

Example fix

// before
const view = table.view();
table.delete();
await view.to_json(); // PerspectiveViewNotFoundException
// after
const view = table.view();
await view.to_json();
view.delete();
table.delete();
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm the view handle is still usable before dependent ops
if (!view || typeof view.table_id === "undefined") {
  view = table.view(); // recreate
}

Try / catch

try {
  await someOperationUsingView(view);
} catch (e) {
  if (String(e).includes("ViewNotFound")) {
    view = table.view(); // recreate the view and retry once
  }
}

Prevention

When it happens

Trigger: Calling a table-level operation (e.g. `table_from_view`, delete paths, or any handler that resolves a view back to its table) with a view ID that is not registered in `m_view_to_table` — after the owning table was deleted, after the view was already deleted, or with a fabricated/uninitialized ID.

Common situations: Client holds a reference to a `view()` whose table was deleted server-side; WebSocket session reconnected after server restart so old view IDs no longer exist; race where one code path deletes the table/view while another still uses the view handle.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

        if (!m_deleted_tables.contains(imap.first)) {
            vec.push_back(imap.first);
        }
    }

    return vec;
}

std::shared_ptr<Table>
ServerResources::get_table_for_view(const t_id& view_id) {
    PSP_READ_LOCK(m_write_lock);
    return m_tables.at(m_view_to_table.at(view_id));
}

ServerResources::t_id
ServerResources::get_table_id_for_view(const t_id& view_id) {
    PSP_READ_LOCK(m_write_lock);
    if (!m_view_to_table.contains(view_id)) {
        throw PerspectiveViewNotFoundException();
    }

    return m_view_to_table.at(view_id);
}

std::vector<ServerResources::t_id>
ServerResources::get_view_ids(const t_id& table_id) {
    PSP_READ_LOCK(m_write_lock);
    std::vector<t_id> out;
    auto range = m_table_to_view.equal_range(table_id);
    for (auto it = range.first; it != range.second; ++it) {
        out.push_back(it->second);
    }
    return out;
}

bool
ServerResources::has_view(const t_id& id) {

View on GitHub (pinned to 11c8238c0c)