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()); + } }