fix: route MCP time-range validation errors to isError response, not ERROR log #SUPERLOG - #418
Open
superlog-app[bot] wants to merge 1 commit into
Open
fix: route MCP time-range validation errors to isError response, not ERROR log #SUPERLOG#418superlog-app[bot] wants to merge 1 commit into
superlog-app[bot] wants to merge 1 commit into
Conversation
…ERROR log #SUPERLOG Delivery-Id: 8d2572a0bb639e12b503ffc0633fea9e7ece6ff58f01ea1a2ee7222a401544ee Delivery-Base: main
| performance.now() - startedAt, | ||
| ); | ||
| onPermanentFailure?.(error); | ||
| // Input-validation errors (bad caller-supplied time range) are not backend |
Contributor
Author
There was a problem hiding this comment.
metrics · blocking — Record validation errors under a distinct outcome label, not "permanent_failure"
Move the recordTelemetryQueryOutcome call (or add a branch) so that TimeRangeValidationError is recorded with outcome "validation_error" instead of "permanent_failure"; otherwise every bad caller time-range inflates the permanent-failure counter and breaks alert thresholds that operators rely on to detect real backend failures.
Suggested change
| // Input-validation errors (bad caller-supplied time range) are not backend | |
| if (isInputValidationError(error)) { | |
| recordTelemetryQueryOutcome( | |
| tool, | |
| "validation_error", | |
| performance.now() - startedAt, | |
| ); | |
| } else { | |
| recordTelemetryQueryOutcome( | |
| tool, | |
| "permanent_failure", | |
| performance.now() - startedAt, | |
| ); | |
| onPermanentFailure?.(error); | |
| } | |
| throw error; |
Useful? React with 👍 / 👎.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MCP telemetry tools (
query_logs,query_traces,query_metrics,list_services) were firingERROR: MCP telemetry query failed permanentlylog entries whenever a caller supplied an invalidsince/untiltime bound — including SQL injection probe strings likenow()) UNION ALL SELECT hostName(),1,1 FROM system.one --. This created noisy incidents and alert fatigue.The validation itself was correct and working:
timeBoundExprvalidates the time bound before any SQL is assembled and throws for anything other than an ISO-8601 timestamp or a recognisednow() - INTERVAL N UNITexpression. No injection ever reached ClickHouse. The bug was that this input-validation error was then routed through the same permanent-failure path as genuine backend errors, triggeringlogger.errorand the incident.Root cause
In
executeRecoverableTelemetryQuery, all errors that failisRetryableTelemetryTimeoutunconditionally callonPermanentFailure, which in the MCP server logs at ERROR level. There was no distinction between "ClickHouse returned an unknown table error" and "the caller passed a garbage time string".Fix
packages/telemetry-query/src/index.ts—timeBoundExprnow throws a namedTimeRangeValidationErrorsubclass instead of a plainError. This lets callers distinguish input-validation failures from backend failures without importing the error class (duck-typing onerror.name).apps/api/src/mcp/telemetry-recovery.ts—executeRecoverableTelemetryQueryskips theonPermanentFailurecallback when the thrown error is aTimeRangeValidationError(checked byerror.name). The error is still re-thrown so the MCP layer can handle it explicitly.apps/api/src/mcp/server.ts—executeTelemetryQuerynow wrapsexecuteRecoverableTelemetryQueryin a try/catch.TimeRangeValidationErroris caught, logged at WARN, and converted to a structured{ content, isError: true }MCP tool result with a human-readable message pointing the AI to valid syntax. All four tool handlers (query_logs,query_traces,query_metrics,list_services) check for the_mcpErrorsentinel and surface it correctly.apps/api/src/mcp/telemetry-recovery.test.ts— two new tests verify thatonPermanentFailureis not called for validation errors, and that the error is still re-thrown.Incident: https://app.superlog.sh/incidents/cda37b62-423a-4a89-a330-6f0463135a2c
Was this PR helpful? Leave feedback — goes straight to the Superlog team.
Summary by cubic
Invalid MCP telemetry time ranges now return a structured
isErrorresponse with guidance instead of logging a permanent ERROR and triggering incidents. This adds a dedicated validation error to separate bad input from backend failures.TimeRangeValidationErrorin@superlog/telemetry-queryand throw it fromtimeBoundExpr.executeRecoverableTelemetryQueryskipsonPermanentFailurefor validation errors but re-throws.{ content, isError: true }forquery_logs,query_traces,query_metrics,list_services.Written for commit 4b59131. Summary will update on new commits.