Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
36 changes: 35 additions & 1 deletion src/core/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:",
Expand All @@ -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
Expand Down Expand Up @@ -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());
}
}