diff --git a/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts b/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts index 9e96aa3599339..04f2cc0b1cc28 100644 --- a/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts +++ b/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts @@ -45,7 +45,14 @@ export const propertyFilterLogic = kea([ listeners(({ actions, props, values }) => ({ // Only send update if value is set to something setFilter: async ({ property }) => { - if (props.sendAllKeyUpdates || property?.value || (property?.key && property.type === 'hogql')) { + if ( + props.sendAllKeyUpdates || + property?.value || + ('operator' in property && + property?.operator && + ['is_set', 'is_not_set'].includes(property?.operator)) || + (property?.key && property.type === 'hogql') + ) { actions.update() } }, diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index 2595e33fe8f8b..1dfc2cc6530dc 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -2336,6 +2336,10 @@ "FunnelsQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -2720,7 +2724,7 @@ "type": "array" }, "error": { - "description": "Query error. Returned only if 'explain' is true. Throws an error otherwise.", + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", "type": "string" }, "explain": { @@ -3275,6 +3279,10 @@ "LifecycleQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -3574,6 +3582,10 @@ "PathsQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -3808,6 +3820,10 @@ "QueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4119,7 +4135,7 @@ "type": "array" }, "error": { - "description": "Query error. Returned only if 'explain' is true. Throws an error otherwise.", + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", "type": "string" }, "explain": { @@ -4238,6 +4254,10 @@ { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4279,6 +4299,10 @@ "items": {}, "type": "array" }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hasMore": { "type": "boolean" }, @@ -4331,6 +4355,10 @@ "items": {}, "type": "array" }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4370,6 +4398,10 @@ { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4404,6 +4436,10 @@ { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4438,6 +4474,10 @@ { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4472,6 +4512,10 @@ { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -4871,6 +4915,10 @@ "RetentionQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -5268,6 +5316,10 @@ "StickinessQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -5605,6 +5657,10 @@ "TrendsQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -5790,6 +5846,10 @@ "WebOverviewQueryResponse": { "additionalProperties": false, "properties": { + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, @@ -5905,6 +5965,10 @@ "items": {}, "type": "array" }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hasMore": { "type": "boolean" }, @@ -5995,6 +6059,10 @@ "items": {}, "type": "array" }, + "error": { + "description": "Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + "type": "string" + }, "hogql": { "type": "string" }, diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index c716913253e99..c1d0c35c7067b 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -209,7 +209,7 @@ export interface HogQLQueryResponse { clickhouse?: string /** Query results */ results?: any[] - /** Query error. Returned only if 'explain' is true. Throws an error otherwise. */ + /** Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise. */ error?: string /** Returned columns */ columns?: any[] @@ -896,6 +896,8 @@ export interface QueryResponse { results: unknown timings?: QueryTiming[] hogql?: string + /** Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise. */ + error?: string is_cached?: boolean last_refresh?: string next_allowed_client_refresh?: string diff --git a/frontend/src/scenes/debug/DebugScene.tsx b/frontend/src/scenes/debug/DebugScene.tsx index 9742920c4af44..829dc2031b3ea 100644 --- a/frontend/src/scenes/debug/DebugScene.tsx +++ b/frontend/src/scenes/debug/DebugScene.tsx @@ -60,11 +60,11 @@ export function DebugScene(): JSX.Element { />
- +
{query2 ? (
- +
) : null}
diff --git a/frontend/src/scenes/debug/DebugSceneQuery.tsx b/frontend/src/scenes/debug/DebugSceneQuery.tsx index ae0fccd2d0d0d..1f4b3db89ea1c 100644 --- a/frontend/src/scenes/debug/DebugSceneQuery.tsx +++ b/frontend/src/scenes/debug/DebugSceneQuery.tsx @@ -4,15 +4,17 @@ import { Modifiers } from 'scenes/debug/Modifiers' import { QueryTabs } from 'scenes/debug/QueryTabs' import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' +import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' import { QueryEditor } from '~/queries/QueryEditor/QueryEditor' import { DataNode, HogQLQuery, Node } from '~/queries/schema' import { isDataTableNode, isInsightVizNode } from '~/queries/utils' interface DebugSceneQueryProps { - queryKey: string + queryKey: `new-${string}` query: string setQuery: (query: string) => void } + export function DebugSceneQuery({ query, setQuery, queryKey }: DebugSceneQueryProps): JSX.Element { let parsed: Record | null = null try { @@ -23,9 +25,10 @@ export function DebugSceneQuery({ query, setQuery, queryKey }: DebugSceneQueryPr const dataNode = parsed && (isInsightVizNode(parsed as Node) || isDataTableNode(parsed as Node)) ? parsed.source : parsed + const dataNodeKey = insightVizDataNodeKey({ dashboardItemId: queryKey }) const dataNodeLogicProps: DataNodeLogicProps = { query: dataNode as DataNode, - key: queryKey, + key: dataNodeKey, dataNodeCollectionId: queryKey, modifiers: { debug: true }, } diff --git a/frontend/src/scenes/debug/HogQLDebug.tsx b/frontend/src/scenes/debug/HogQLDebug.tsx index dab62f85b68cb..b2dcb340c1d45 100644 --- a/frontend/src/scenes/debug/HogQLDebug.tsx +++ b/frontend/src/scenes/debug/HogQLDebug.tsx @@ -13,7 +13,7 @@ import { DataNode, HogQLQuery, HogQLQueryResponse } from '~/queries/schema' import { QueryTabs } from './QueryTabs' interface HogQLDebugProps { - queryKey: string + queryKey: `new-${string}` query: HogQLQuery setQuery: (query: DataNode) => void } diff --git a/frontend/src/scenes/debug/QueryTabs.tsx b/frontend/src/scenes/debug/QueryTabs.tsx index a4185467c1f79..26beac0ef3eff 100644 --- a/frontend/src/scenes/debug/QueryTabs.tsx +++ b/frontend/src/scenes/debug/QueryTabs.tsx @@ -35,7 +35,7 @@ function toColumn(hogql: string, position: number): number { } interface QueryTabsProps { query: Node - queryKey: string + queryKey: `new-${string}` response?: Record | null setQuery: (query: DataNode) => void } @@ -62,7 +62,21 @@ export function QueryTabs({ query, queryKey, setQuery, response }: QueryTabsProp isInsightVizNode(query) && { key: 'viz', label: 'Visualization', - content: setQuery(query)} />, + content: ( + setQuery(query)} + context={{ + insightProps: { + dashboardItemId: queryKey, + query, + setQuery: (query) => setQuery(query), + dataNodeCollectionId: queryKey, + }, + }} + /> + ), }, isInsightQueryNode(query) && { key: 'insight', diff --git a/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts b/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts index 45b6dffcc98ed..e228148d801f9 100644 --- a/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts +++ b/plugin-server/src/main/ingestion-queues/session-recording/session-recordings-consumer.ts @@ -335,7 +335,7 @@ export class SessionRecordingIngester { }) await runInstrumentedFunction({ statsKey: `recordingingester.handleEachBatch`, - logExecutionTime: true, + sendTimeoutGuardToSentry: false, func: async () => { histogramKafkaBatchSize.observe(messages.length) histogramKafkaBatchSizeKb.observe(messages.reduce((acc, m) => (m.value?.length ?? 0) + acc, 0) / 1024) @@ -646,7 +646,6 @@ export class SessionRecordingIngester { const startTime = Date.now() await runInstrumentedFunction({ statsKey: `recordingingester.onRevokePartitions.revokeSessions`, - logExecutionTime: true, timeout: SHUTDOWN_FLUSH_TIMEOUT_MS, // same as the partition lock func: async () => { if (this.config.SESSION_RECORDING_PARTITION_REVOKE_OPTIMIZATION) { diff --git a/plugin-server/src/main/utils.ts b/plugin-server/src/main/utils.ts index caeaeb2146109..76265930c8b3d 100644 --- a/plugin-server/src/main/utils.ts +++ b/plugin-server/src/main/utils.ts @@ -12,6 +12,7 @@ interface FunctionInstrumentation { timeoutContext?: () => Record teamId?: number logExecutionTime?: boolean + sendTimeoutGuardToSentry?: boolean } const logTime = (startTime: number, statsKey: string, error?: any) => { @@ -30,8 +31,14 @@ export async function runInstrumentedFunction({ statsKey, teamId, logExecutionTime = false, + sendTimeoutGuardToSentry = true, }: FunctionInstrumentation): Promise { - const t = timeoutGuard(timeoutMessage ?? `Timeout warning for '${statsKey}'!`, timeoutContext, timeout) + const t = timeoutGuard( + timeoutMessage ?? `Timeout warning for '${statsKey}'!`, + timeoutContext, + timeout, + sendTimeoutGuardToSentry + ) const startTime = performance.now() const end = instrumentedFunctionDuration.startTimer({ function: statsKey, diff --git a/posthog/clickhouse/client/execute_async.py b/posthog/clickhouse/client/execute_async.py index 2a2e762d5aa56..1c93ee3a0c0c9 100644 --- a/posthog/clickhouse/client/execute_async.py +++ b/posthog/clickhouse/client/execute_async.py @@ -4,6 +4,7 @@ from typing import Optional import uuid +from sentry_sdk import push_scope import structlog from prometheus_client import Histogram from rest_framework.exceptions import NotFound @@ -85,50 +86,58 @@ def execute_process_query( from posthog.api.services.query import process_query, ExecutionMode from posthog.models import Team + from posthog.models.user import User team = Team.objects.get(pk=team_id) + user = User.objects.get(pk=user_id) - query_status = manager.get_query_status() + with push_scope() as scope: + scope.set_user({"email": user.email, "id": user_id, "username": user.email}) + scope.set_tag("team_id", team_id) - if query_status.complete or query_status.error: - return + query_status = manager.get_query_status() - query_status.error = True # Assume error in case nothing below ends up working + if query_status.complete or query_status.error: + return - pickup_time = datetime.datetime.now(datetime.timezone.utc) - if query_status.start_time: - wait_duration = (pickup_time - query_status.start_time) / datetime.timedelta(seconds=1) - QUERY_WAIT_TIME.observe(wait_duration) + query_status.error = True # Assume error in case nothing below ends up working - try: - tag_queries(client_query_id=query_id, team_id=team_id, user_id=user_id) - results = process_query( - team=team, - query_json=query_json, - limit_context=limit_context, - execution_mode=ExecutionMode.CALCULATION_ALWAYS - if refresh_requested - else ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, - ) - logger.info("Got results for team %s query %s", team_id, query_id) - query_status.complete = True - query_status.error = False - query_status.results = results - query_status.end_time = datetime.datetime.now(datetime.timezone.utc) - query_status.expiration_time = query_status.end_time + datetime.timedelta(seconds=manager.STATUS_TTL_SECONDS) - process_duration = (query_status.end_time - pickup_time) / datetime.timedelta(seconds=1) - QUERY_PROCESS_TIME.observe(process_duration) - except (ExposedHogQLError, ExposedCHQueryError) as err: # We can expose the error to the user - query_status.results = None # Clear results in case they are faulty - query_status.error_message = str(err) - logger.error("Error processing query for team %s query %s: %s", team_id, query_id, err) - raise err - except Exception as err: # We cannot reveal anything about the error - query_status.results = None # Clear results in case they are faulty - logger.error("Error processing query for team %s query %s: %s", team_id, query_id, err) - raise err - finally: - manager.store_query_status(query_status) + pickup_time = datetime.datetime.now(datetime.timezone.utc) + if query_status.start_time: + wait_duration = (pickup_time - query_status.start_time) / datetime.timedelta(seconds=1) + QUERY_WAIT_TIME.observe(wait_duration) + + try: + tag_queries(client_query_id=query_id, team_id=team_id, user_id=user_id) + results = process_query( + team=team, + query_json=query_json, + limit_context=limit_context, + execution_mode=ExecutionMode.CALCULATION_ALWAYS + if refresh_requested + else ExecutionMode.RECENT_CACHE_CALCULATE_IF_STALE, + ) + logger.info("Got results for team %s query %s", team_id, query_id) + query_status.complete = True + query_status.error = False + query_status.results = results + query_status.end_time = datetime.datetime.now(datetime.timezone.utc) + query_status.expiration_time = query_status.end_time + datetime.timedelta( + seconds=manager.STATUS_TTL_SECONDS + ) + process_duration = (query_status.end_time - pickup_time) / datetime.timedelta(seconds=1) + QUERY_PROCESS_TIME.observe(process_duration) + except (ExposedHogQLError, ExposedCHQueryError) as err: # We can expose the error to the user + query_status.results = None # Clear results in case they are faulty + query_status.error_message = str(err) + logger.error("Error processing query for team %s query %s: %s", team_id, query_id, err) + raise err + except Exception as err: # We cannot reveal anything about the error + query_status.results = None # Clear results in case they are faulty + logger.error("Error processing query for team %s query %s: %s", team_id, query_id, err) + raise err + finally: + manager.store_query_status(query_status) def kick_off_task( diff --git a/posthog/clickhouse/client/test/test_execute_async.py b/posthog/clickhouse/client/test/test_execute_async.py index 6da8a370b0bf2..b31123a152cdd 100644 --- a/posthog/clickhouse/client/test/test_execute_async.py +++ b/posthog/clickhouse/client/test/test_execute_async.py @@ -7,6 +7,7 @@ from posthog.client import sync_execute from posthog.hogql.errors import ExposedHogQLError from posthog.models import Organization, Team +from posthog.models.user import User from posthog.test.base import ClickhouseTestMixin, snapshot_clickhouse_queries from unittest.mock import patch, MagicMock from posthog.clickhouse.client.execute_async import QueryStatusManager, execute_process_query @@ -21,10 +22,11 @@ def build_query(sql): class TestExecuteProcessQuery(TestCase): def setUp(self): + user = User.objects.create(email="test@posthog.com") self.organization = Organization.objects.create(name="test") self.team = Team.objects.create(organization=self.organization) self.team_id = self.team.pk - self.user_id = 1337 + self.user_id: int = user.id self.query_id = "test_query_id" self.query_json = {} self.limit_context = None @@ -58,10 +60,11 @@ def test_execute_process_query(self, mock_process_query, mock_redis_client): class ClickhouseClientTestCase(TestCase, ClickhouseTestMixin): def setUp(self): + user = User.objects.create(email="test@posthog.com", id=1337) self.organization: Organization = Organization.objects.create(name="test") self.team: Team = Team.objects.create(organization=self.organization) self.team_id: int = self.team.pk - self.user_id: int = 2137 + self.user_id: int = user.id @snapshot_clickhouse_queries def test_async_query_client(self): diff --git a/posthog/hogql/database/database.py b/posthog/hogql/database/database.py index fc1f665cf3978..f3b8e76235f21 100644 --- a/posthog/hogql/database/database.py +++ b/posthog/hogql/database/database.py @@ -137,7 +137,9 @@ def get_table(self, table_name: str) -> Table: raise QueryError(f'Unknown table "{table_name}".') def get_all_tables(self) -> list[str]: - return self._table_names + self._warehouse_table_names + all_keys = list(vars(self).keys()) + table_names = [key for key in all_keys if isinstance(getattr(self, key), Table)] + return table_names + self._warehouse_table_names def add_warehouse_tables(self, **field_definitions: Any): for f_name, f_def in field_definitions.items(): diff --git a/posthog/hogql_queries/insights/trends/trends_query_runner.py b/posthog/hogql_queries/insights/trends/trends_query_runner.py index 4425c4c88f580..42f1654da71fd 100644 --- a/posthog/hogql_queries/insights/trends/trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/trends_query_runner.py @@ -292,6 +292,7 @@ def calculate(self): res_matrix: list[list[Any] | Any | None] = [None] * len(queries) timings_matrix: list[list[QueryTiming] | None] = [None] * len(queries) errors: list[Exception] = [] + debug_errors: list[str] = [] def run(index: int, query: ast.SelectQuery | ast.SelectUnionQuery, is_parallel: bool): try: @@ -308,6 +309,8 @@ def run(index: int, query: ast.SelectQuery | ast.SelectUnionQuery, is_parallel: timings_matrix[index] = response.timings res_matrix[index] = self.build_series_response(response, series_with_extra, len(queries)) + if response.error: + debug_errors.append(response.error) except Exception as e: errors.append(e) finally: @@ -362,7 +365,9 @@ def run(index: int, query: ast.SelectQuery | ast.SelectUnionQuery, is_parallel: with self.timings.measure("apply_formula"): res = self.apply_formula(self.query.trendsFilter.formula, res) - return TrendsQueryResponse(results=res, timings=timings, hogql=response_hogql, modifiers=self.modifiers) + return TrendsQueryResponse( + results=res, timings=timings, hogql=response_hogql, modifiers=self.modifiers, error=". ".join(debug_errors) + ) def build_series_response(self, response: HogQLQueryResponse, series: SeriesWithExtras, series_count: int): if response.results is None: diff --git a/posthog/hogql_queries/query_runner.py b/posthog/hogql_queries/query_runner.py index babac93129ef2..02e2f18593453 100644 --- a/posthog/hogql_queries/query_runner.py +++ b/posthog/hogql_queries/query_runner.py @@ -83,6 +83,7 @@ class QueryResponse(BaseModel, Generic[DataT]): timings: Optional[list[QueryTiming]] = None types: Optional[list[Union[tuple[str, str], str]]] = None columns: Optional[list[str]] = None + error: Optional[str] = None hogql: Optional[str] = None hasMore: Optional[bool] = None limit: Optional[int] = None @@ -397,7 +398,12 @@ def run( fresh_response_dict["cache_key"] = cache_key fresh_response_dict["timezone"] = self.team.timezone fresh_response = CachedQueryResponse(**fresh_response_dict) - cache.set(cache_key, fresh_response, settings.CACHED_RESULTS_TTL) + + # Dont cache debug queries with errors + has_error = fresh_response_dict.get("error", None) + if has_error is None or len(has_error) == 0: + cache.set(cache_key, fresh_response, settings.CACHED_RESULTS_TTL) + QUERY_CACHE_WRITE_COUNTER.labels(team_id=self.team.pk).inc() return fresh_response diff --git a/posthog/hogql_queries/web_analytics/stats_table.py b/posthog/hogql_queries/web_analytics/stats_table.py index 3f36f000628a1..ee6b06e9177a5 100644 --- a/posthog/hogql_queries/web_analytics/stats_table.py +++ b/posthog/hogql_queries/web_analytics/stats_table.py @@ -3,7 +3,13 @@ from posthog.hogql import ast from posthog.hogql.constants import LimitContext from posthog.hogql.parser import parse_select, parse_expr -from posthog.hogql.property import property_to_expr, get_property_operator, get_property_value, get_property_type +from posthog.hogql.property import ( + property_to_expr, + get_property_operator, + get_property_value, + get_property_type, + get_property_key, +) from posthog.hogql_queries.insights.paginators import HogQLHasMorePaginator from posthog.hogql_queries.web_analytics.web_analytics_query_runner import ( WebAnalyticsQueryRunner, @@ -366,7 +372,7 @@ def _event_properties(self) -> ast.Expr: def _event_properties_for_scroll(self) -> ast.Expr: def map_scroll_property(property: Union[EventPropertyFilter, PersonPropertyFilter]): - if get_property_type(property) == "event" and property.key == "$pathname": + if get_property_type(property) == "event" and get_property_key(property) == "$pathname": return EventPropertyFilter( key="$prev_pageview_pathname", operator=get_property_operator(property), diff --git a/posthog/schema.py b/posthog/schema.py index 6cbdda24026cc..655b41f356bfd 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -858,6 +858,10 @@ class StickinessQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -952,6 +956,10 @@ class TrendsQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1016,6 +1024,10 @@ class WebOverviewQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1050,6 +1062,10 @@ class WebStatsTableQueryResponse(BaseModel): extra="forbid", ) columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hasMore: Optional[bool] = None hogql: Optional[str] = None is_cached: Optional[bool] = None @@ -1069,6 +1085,10 @@ class WebTopClicksQueryResponse(BaseModel): extra="forbid", ) columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1256,6 +1276,10 @@ class FunnelsQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1318,7 +1342,8 @@ class HogQLQueryResponse(BaseModel): clickhouse: Optional[str] = Field(default=None, description="Executed ClickHouse query") columns: Optional[list] = Field(default=None, description="Returned columns") error: Optional[str] = Field( - default=None, description="Query error. Returned only if 'explain' is true. Throws an error otherwise." + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", ) explain: Optional[list[str]] = Field(default=None, description="Query explanation output") hasMore: Optional[bool] = None @@ -1368,6 +1393,10 @@ class LifecycleQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1388,6 +1417,10 @@ class PathsQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1412,6 +1445,10 @@ class QueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1469,7 +1506,8 @@ class QueryResponseAlternative7(BaseModel): clickhouse: Optional[str] = Field(default=None, description="Executed ClickHouse query") columns: Optional[list] = Field(default=None, description="Returned columns") error: Optional[str] = Field( - default=None, description="Query error. Returned only if 'explain' is true. Throws an error otherwise." + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", ) explain: Optional[list[str]] = Field(default=None, description="Query explanation output") hasMore: Optional[bool] = None @@ -1503,6 +1541,10 @@ class QueryResponseAlternative10(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1518,6 +1560,10 @@ class QueryResponseAlternative11(BaseModel): extra="forbid", ) columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hasMore: Optional[bool] = None hogql: Optional[str] = None is_cached: Optional[bool] = None @@ -1537,6 +1583,10 @@ class QueryResponseAlternative12(BaseModel): extra="forbid", ) columns: Optional[list] = None + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -1552,6 +1602,10 @@ class QueryResponseAlternative13(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -2309,6 +2363,10 @@ class QueryResponseAlternative14(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None @@ -2366,6 +2424,10 @@ class RetentionQueryResponse(BaseModel): model_config = ConfigDict( extra="forbid", ) + error: Optional[str] = Field( + default=None, + description="Query error. Returned only if 'explain' or `modifiers.debug` is true. Throws an error otherwise.", + ) hogql: Optional[str] = None is_cached: Optional[bool] = None last_refresh: Optional[str] = None