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
- Keep the parent table alive as long as any of its views are in use; delete views before deleting tables
- Wrap view operations in error handling that recreates the view (`table.view(...)`) when the server reports a missing view
- Avoid reusing view handles across server restarts/reconnects — recreate table and view after reconnecting
- 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
- Delete views before deleting their parent tables
- Never reuse view handles across server restarts/reconnects
- Avoid concurrent delete + use of the same view from different async paths
- Track view ownership in one place in your app
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)