fix(python): preserve request metadata for missing remote tables

Fixes #4750

Remote missing-table errors retained HTTP metadata in Rust but the Python binding converted them to a plain `ValueError`, discarding the request ID and status. This raises a `TableNotFoundError` that preserves the original message, request ID, status code, and chained HTTP cause while remaining compatible with existing `ValueError` and `lance_namespace.errors.TableNotFoundError` handlers. Local and non-HTTP errors retain their previous behavior.

Validation covers sync and async opens across cached/uncached and root/nested paths, exception pickling, local compatibility, the remote and error test suites, Clippy, formatting, Ruff, and all applicable PR checks including CodeQL.
This commit is contained in:
lancedb-gatefixer[bot]
2026-11-01 11:10:44 +08:00
committed by GitHub
parent fdf886d42a
commit 71b23ed9fb
5 changed files with 133 additions and 2 deletions
+21 -1
View File
@@ -28,12 +28,32 @@ impl<T> PythonErrorExt<T> for std::result::Result<T, LanceError> {
Err(err) => match err {
LanceError::InvalidInput { .. }
| LanceError::InvalidTableName { .. }
| LanceError::TableNotFound { .. }
| LanceError::NotAMaterializedView { .. }
| LanceError::Schema { .. }
| LanceError::DatabaseNotFound { .. }
| LanceError::DatabaseAlreadyExists { .. }
| LanceError::TableAlreadyExists { .. } => self.value_error(),
LanceError::TableNotFound { source, .. } => {
let Some(LanceError::Http {
request_id,
status_code,
..
}) = source.downcast_ref::<LanceError>()
else {
return self.value_error();
};
Python::attach(|py| {
let cls = py
.import(intern!(py, "lancedb.remote.errors"))?
.getattr(intern!(py, "TableNotFoundError"))?;
let status_code = status_code.map(|s| s.as_u16());
let py_err = cls.call1((err.to_string(), request_id, status_code))?;
let cause =
http_from_rust_error(py, source.as_ref(), request_id, status_code)?;
py_err.setattr(intern!(py, "__cause__"), cause)?;
Err(PyErr::from_value(py_err))
})
}
LanceError::CreateDir { .. } => self.os_error(),
LanceError::ObjectStore { .. } => Err(PyIOError::new_err(err.to_string())),
LanceError::NotSupported { .. } => {