windmill-labs/windmill · error

{webhook_key}: {e}

Error message

{webhook_key}: {e}

What it means

sync_global_settings_declarative validates the github_app_webhook_base_url global setting before applying a declarative settings diff. When the desired value is a non-empty string that fails validate_webhook_base_url, the underlying validation error is re-wrapped prefixed with the setting key. This exists so CLI sync-config and the Kubernetes operator reject the same values the HTTP API would reject.

Source

Thrown at backend/windmill-common/src/instance_config.rs:1315

/// workspaces listed in instance settings and re-saves them.
///
/// AUTHORIZATION: replaces instance-wide settings and takes no authed context, so
/// callers MUST have established superadmin or equivalent system authority (the CLI
/// and the operator both run with direct instance credentials).
pub async fn sync_global_settings_declarative(
    db: &sqlx::Pool<sqlx::Postgres>,
    current: &BTreeMap<String, serde_json::Value>,
    desired: &BTreeMap<String, serde_json::Value>,
) -> anyhow::Result<()> {
    let webhook_key = crate::global_settings::GITHUB_APP_WEBHOOK_BASE_URL_SETTING;
    // Non-string shapes are rejected rather than ignored: `as_str()` alone would let a
    // bool/number/object through as if the key were absent, and the diff below would
    // then persist it — where the HTTP path answers "must be a URL string".
    match desired.get(webhook_key) {
        None | Some(serde_json::Value::Null) => {}
        Some(serde_json::Value::String(s)) if s.trim().is_empty() => {}
        Some(serde_json::Value::String(s)) => crate::global_settings::validate_webhook_base_url(s)
            .map_err(|e| anyhow::anyhow!("{webhook_key}: {e}"))?,
        // Names the JSON kind rather than printing it: this is the last message on
        // this path that could report submitted content, and an object or array could
        // carry a secret into `sync-config` output and operator logs.
        Some(other) => {
            let kind = match other {
                serde_json::Value::Bool(_) => "a boolean",
                serde_json::Value::Number(_) => "a number",
                serde_json::Value::Array(_) => "an array",
                serde_json::Value::Object(_) => "an object",
                _ => "a non-string value",
            };
            return Err(anyhow::anyhow!(
                "{webhook_key} must be a URL string, got {kind}"
            ));
        }
    }

    let diff = diff_global_settings(current, desired, ApplyMode::Replace);

View on GitHub (pinned to e474e8803c)

Solutions

  1. Prefix the value with its scheme: use https://host/path (or http only for local testing)
  2. Validate locally: the URL must parse as an absolute http(s) URL
  3. Fix the value in the source config (CLI -c file, ConfigMap, or values.yaml) and re-run sync
  4. If the error text after the prefix is unclear, check validate_webhook_base_url in global_settings for the exact rule

Example fix

// before (config)
github_app_webhook_base_url: windmill.example.com/api/webhooks
// after
github_app_webhook_base_url: https://windmill.example.com/api/webhooks
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_webhook_base_url(s: &str) -> bool {
    s.starts_with("https://") || s.starts_with("http://")
}
// in config before sync:
if let Some(v) = desired.get("github_app_webhook_base_url") {
    let s = v.as_str().expect("webhook base url must be a string");
    assert!(is_valid_webhook_base_url(s), "github_app_webhook_base_url must be an absolute http(s) URL, got {s:?}");
}

Type guard

fn as_url_string(v: &serde_json::Value) -> Option<&str> {
    v.as_str().filter(|s| !s.trim().is_empty())
}

Try / catch

match sync_global_settings_declarative(&db, &current, &desired).await {
    Err(e) if e.to_string().starts_with("github_app_webhook_base_url:") =>
        anyhow::bail!("config rejected: fix github_app_webhook_base_url in your declarative settings — {}", e),
    other => other,
}

Prevention

When it happens

Trigger: A declarative config (settings YAML/ConfigMap for sync-config or the operator) sets github_app_webhook_base_url to a string that is not a valid URL — missing scheme, not absolute, or otherwise rejected by validate_webhook_base_url.

Common situations: Writing windmill.example.com/webhook without https://, typos like htps://, config mistakes in a Helm/K8s values file, copying a relative path instead of a full URL.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/151280bca7768944. Report an issue: GitHub.