From 95acfc728f52cd957d35cd1b329e943855089932 Mon Sep 17 00:00:00 2001 From: Jeffrey Aven Date: Mon, 24 Aug 2026 15:23:30 +1000 Subject: [PATCH] Fail fast on stackql planner errors, bump to 2.1.1 Planner/compiler errors (could not locate symbol, cannot find matching operation, syntax error at position, disparity in fields to insert) are deterministic and can never succeed on retry, but were swallowed by the exists/statecheck retry loop and retried until the budget was exhausted. They are now classified as fatal in check_fatal_error and abort the operation immediately. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 6 ++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- src/core/errors.rs | 36 +++++++++++++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a37bef2..870729f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 2.1.1 (2026-08-24) + +### Fixes + +- StackQL planner/compiler errors (`could not locate symbol ...`, `cannot find matching operation ...`, `syntax error at position ...`, `disparity in fields to insert ...`) are now treated as fatal and abort the operation immediately. These errors are deterministic - the same query fails the same way on every attempt - but they were previously swallowed by the `exists`/`statecheck` retry loop and retried until the retry budget was exhausted. + ## 2.1.0 (2026-08-24) ### Features diff --git a/Cargo.lock b/Cargo.lock index 0ddd055..1e219cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1809,7 +1809,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stackql-deploy" -version = "2.1.0" +version = "2.1.1" dependencies = [ "base64", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 564bda4..b032b24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stackql-deploy" -version = "2.1.0" +version = "2.1.1" edition = "2021" rust-version = "1.75" description = "Infrastructure-as-code framework for declarative cloud resource management using StackQL" diff --git a/src/core/errors.rs b/src/core/errors.rs index 03a9370..794ff87 100644 --- a/src/core/errors.rs +++ b/src/core/errors.rs @@ -10,7 +10,7 @@ /// StackQL engine. If any pattern matches, the operation is aborted /// immediately rather than retried. /// -/// Two categories: +/// Three categories: /// /// 1. **Network errors** - The request never reached the API. Any result /// from a query in this state is untrustworthy (e.g., an exists check @@ -19,6 +19,11 @@ /// 2. **HTTP status errors** - The request reached the API but the response /// indicates an unrecoverable problem (auth failure, forbidden, etc.). /// 404 is explicitly excluded as it's normal for exists checks. +/// +/// 3. **Planner/compiler errors** - The stackql engine could not plan the +/// query (unknown column/symbol, no matching provider operation, SQL +/// syntax error). These are deterministic: the same query will fail the +/// same way on every attempt, so retrying only burns the retry budget. const FATAL_ERROR_PATTERNS: &[&str] = &[ // Network-layer errors (Go net/http) "dial tcp:", @@ -36,6 +41,11 @@ const FATAL_ERROR_PATTERNS: &[&str] = &[ // HTTP status codes that are never retryable "http response status code: 401", "http response status code: 403", + // stackql planner/compiler errors - deterministic, never succeed on retry + "could not locate symbol", + "cannot find matching operation", + "disparity in fields to insert", + "syntax error at position", ]; /// Patterns that indicate a non-fatal error, even if a fatal pattern @@ -119,4 +129,28 @@ mod tests { let msg = r#"query returns error: no such column: foo"#; assert!(check_fatal_error(msg).is_none()); } + + #[test] + fn test_could_not_locate_symbol_is_fatal() { + let msg = r#"Query execution failed: could not locate symbol DBInstanceStatus"#; + assert!(check_fatal_error(msg).is_some()); + } + + #[test] + fn test_cannot_find_matching_operation_is_fatal() { + let msg = r#"cannot find matching operation, searched: [insert update delete]"#; + assert!(check_fatal_error(msg).is_some()); + } + + #[test] + fn test_syntax_error_is_fatal() { + let msg = r#"Query execution failed: syntax error at position 42 near 'form'"#; + assert!(check_fatal_error(msg).is_some()); + } + + #[test] + fn test_disparity_in_fields_is_fatal() { + let msg = r#"disparity in fields to insert and supplied data"#; + assert!(check_fatal_error(msg).is_some()); + } }