Summary
Since a Looker instance update to 26.14.6, spectacles sql --branch ... (dev mode) crashes the entire run with:
Encountered an unexpected query result format. Unable to extract error details
from the Looker API's response. The unexpected response has been logged.
pydantic ValidationError: 2 validation errors for QueryResult
__root__ -> ErrorQueryResult -> data -> error field required
__root__ -> ErrorQueryResult -> data -> runtime field required
Cause
In dev mode, query results served from cache for queries touching derived tables with dev-mode filters (-- if dev -- conditional SQL) now come back with status: "error" carrying only the non-fatal warning note — and no runtime field:
{
"status": "error",
"result_source": "query",
"data": {
"from_cache": true,
"id": "4bc672d22193f6a2dfe7304e82854c83",
"errors": [
{
"message": "Note: This query contains derived tables with Development Mode filters. Query results in Production Mode might be different.",
"message_details": null,
"params": null,
"edit_url": null,
"error_pos": null,
"fatal": false,
"level": "error",
"login_required_oauth_application_id": null
}
],
"data": []
}
}
ErrorQueryResult.MultiErrorData declares runtime: float with no default, so parsing fails — one layer before get_valid_errors() would have excluded this exact note text (it's already in the WARNINGS tuple). The ErrorData variant doesn't match either (no error string).
Affected: 2.3.x and 2.4.x through v2.4.20 and current master (spectacles/models.py — MultiErrorData.runtime still required).
Suggested fix
One line — give the field a default so the cached-note payload parses, after which the existing warning exclusion handles it:
class MultiErrorData(BaseModel):
id: str
runtime: float = 0.0
sql: Optional[str]
errors: Optional[Tuple[QueryError, ...]]
Verified locally against spectacles 2.3.15 with the payload above: unpatched parse fails as in CI; with the default the payload parses, get_valid_errors() returns empty, and genuine SQL error payloads (which include runtime) still surface. Happy to open a PR.
Environment
- spectacles 2.3.15 (also reproduced against 2.4.18/2.4.20 model definitions)
- Looker 26.14.6, API 4.0
- Command:
spectacles sql --project <p> --branch <b> --remote-reset --ignore-hidden --fail-fast --concurrency 20
Summary
Since a Looker instance update to 26.14.6,
spectacles sql --branch ...(dev mode) crashes the entire run with:Cause
In dev mode, query results served from cache for queries touching derived tables with dev-mode filters (
-- if dev --conditional SQL) now come back withstatus: "error"carrying only the non-fatal warning note — and noruntimefield:{ "status": "error", "result_source": "query", "data": { "from_cache": true, "id": "4bc672d22193f6a2dfe7304e82854c83", "errors": [ { "message": "Note: This query contains derived tables with Development Mode filters. Query results in Production Mode might be different.", "message_details": null, "params": null, "edit_url": null, "error_pos": null, "fatal": false, "level": "error", "login_required_oauth_application_id": null } ], "data": [] } }ErrorQueryResult.MultiErrorDatadeclaresruntime: floatwith no default, so parsing fails — one layer beforeget_valid_errors()would have excluded this exact note text (it's already in theWARNINGStuple). TheErrorDatavariant doesn't match either (noerrorstring).Affected: 2.3.x and 2.4.x through v2.4.20 and current master (
spectacles/models.py—MultiErrorData.runtimestill required).Suggested fix
One line — give the field a default so the cached-note payload parses, after which the existing warning exclusion handles it:
Verified locally against spectacles 2.3.15 with the payload above: unpatched parse fails as in CI; with the default the payload parses,
get_valid_errors()returns empty, and genuine SQL error payloads (which includeruntime) still surface. Happy to open a PR.Environment
spectacles sql --project <p> --branch <b> --remote-reset --ignore-hidden --fail-fast --concurrency 20