dbt-labs/dbt-core · info

valid regex

Error message

valid regex

What it means

A `Regex::new(r"(?i)__dbt_tmp").expect("valid regex")` inside a `LazyLock` used to mark temp-relation identifiers carrying non-deterministic `__dbt_tmp` suffixes. `expect` here only fires if the regex fails to COMPILE. Because the pattern is a compile-time constant and is known-valid, this panic is unreachable in any build containing this exact source.

Source

Thrown at crates/dbt-adapter/src/time_machine/event_replay.rs:33

use flate2::read::GzDecoder;
use parking_lot::RwLock;
use regex::Regex;
use serde::{Deserialize, Serialize};
use similar::{ChangeTag, TextDiff};

use super::event::{
    AdapterCallEvent, CacheInvalidationEvent, MetadataCallArgs, MetadataCallEvent, RecordedEvent,
    RecordingHeader, RunCacheCloneEvent, RunRemoteAdhocEvent, SaoEvent,
};
use super::semantic::SemanticCategory;
use super::serde::values_match;
use super::validation::{SqlSanitizer, TmpSuffixSanitizer, UuidSanitizer};
use crate::AdapterType;
use crate::sql::diff::{canonicalize_python_model_pair, compare_sql};

/// Marker for temp-relation identifiers, which carry a non-deterministic suffix.
static TMP_MARKER_RE: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r"(?i)__dbt_tmp").expect("valid regex"));

/// Extract the SQL string from args (first string in array, or the string itself).
///
/// For execute/run_query, args are serialized as `[sql, auto_begin, fetch, limit, options]`
/// so SQL is the first element of the array.
fn extract_sql_from_args(args: &serde_json::Value) -> Option<&str> {
    match args {
        serde_json::Value::String(s) => Some(s.as_str()),
        serde_json::Value::Array(arr) => arr.first().and_then(|v| v.as_str()),
        _ => None,
    }
}

fn is_sql_method(method: &str) -> bool {
    method == "execute" || method == "run_query"
}

/// Returns true if the SQL string is read-only and cannot mutate DB state.

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Check the pattern literal in crates/dbt-adapter/src/time_machine/event_replay.rs for regex syntax errors (unescaped brackets, unbalanced parens) and fix it.
  2. Test the pattern in a regex debugger (e.g. regex101 with the Rust `regex` crate flavor) before committing changes.
  3. Optionally add a #[test] that constructs TMP_MARKER_RE so a bad pattern fails in CI with a clear test name.
  4. Consider `once_cell`-style compile-time validation via build.rs or the `lazy_static` + test approach to fail earlier.

Example fix

// before (typo introduced during edit)
Regex::new(r"(?i)__dbt_tmp").expect("valid regex")  // OK, but suppose r"(?i)__dbt_tmp(" was committed
// after
Regex::new(r"(?i)__dbt_tmp").expect("TMP_MARKER_RE must be a valid regex")
// plus a guard test:
#[test]
fn tmp_marker_regex_compiles() { LazyLock::force(&TMP_MARKER_RE); }
Defensive patterns

Strategy: validation

Validate before calling

// CI-time check that all LazyLock regexes compile:
#[test]
fn marker_regexes_compile() {
    LazyLock::force(&TMP_MARKER_RE);
    assert!(TMP_MARKER_RE.is_match("my_model__dbt_tmp"));
}

Type guard

fn valid_static_regex(pattern: &str) -> bool { Regex::new(pattern).is_ok() }

Try / catch

std::panic::catch_unwind(|| LazyLock::force(&TMP_MARKER_RE))
    .map_err(|_| anyhow::anyhow!("static regex failed to compile — check event_replay.rs pattern"))?;

Prevention

When it happens

Trigger: Not triggerable by callers at all. Would only panic if the source constant were edited to an invalid regex (e.g. an unbalanced group) — then the first use of TMP_MARKER_RE in event_replay panics at LazyLock initialization time, on the first call to any function referencing it.

Common situations: Only during development: editing the pattern and introducing a regex syntax error; symptoms show up as a first-use panic rather than a compile error, often confusing developers who expect static checking.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/4a4fcd7e87683210. Report an issue: GitHub.