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
- Prefix the value with its scheme: use https://host/path (or http only for local testing)
- Validate locally: the URL must parse as an absolute http(s) URL
- Fix the value in the source config (CLI -c file, ConfigMap, or values.yaml) and re-run sync
- 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, ¤t, &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
- Always write webhook URLs with explicit scheme in config files
- Validate declarative settings locally before running sync-config against production
- Keep declarative config and HTTP-layer validation rules in sync when editing global settings
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
- Invalid flow setting ${path}: ${issue?.message ?? 'unknown e
- {webhook_key} must be a URL string, got {kind}
- Unknown service, currently supported services are: [{}]
- result.substring(__RESULT_ERR_PREFIX.length)
- Invalid migration name '${name}': use only letters, digits,
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/151280bca7768944.
Report an issue: GitHub.