windmill-labs/windmill · error
Expected string, got {:?}
Error message
Expected string, got {:?} What it means
A helper in windmill-store reads a stored value from the resource/variable store and expects the stored JSON to be a string. If the value in the store is any other JSON type (object, number, array, null), this error is thrown with a debug dump of the actual value.
Source
Thrown at backend/windmill-store/src/resources.rs:4118
db: &DB,
w_id: &str,
s: String,
) -> std::result::Result<String, anyhow::Error> {
use serde_json::Value;
use windmill_common::db::DbWithOptAuthed;
let value = Value::String(s);
match transform_json_value(
&DbWithOptAuthed::from_authed(authed, db.clone(), None),
w_id,
value,
&None,
None,
0,
)
.await?
{
Value::String(s) => Ok(s),
v => Err(anyhow::anyhow!("Expected string, got {:?}", v)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::sync::{Arc, Mutex};
use windmill_common::audit::AuditAuthor;
use windmill_common::db::DbWithOptAuthed;
#[test]
fn parse_symref_head_resolves_default_branch() {
let out = "ref: refs/heads/main\tHEAD\n7ddb8cec9a0000000000000000000000000000aa\tHEAD\n";
assert_eq!(
parse_ls_remote_symref_head(out),
(
Some("main".to_string()),View on GitHub (pinned to e474e8803c)
Solutions
- Inspect the stored value at that path (resource/variable page or DB) and confirm its actual JSON type
- Use the appropriate getter for the real type (JSON/object getter) or convert the value to a string
- Fix the resource/variable content so the value at that path is a plain string
- Update caller code to request the value with the matching typed API
- Check recent edits/migrations that could have changed the stored shape
Example fix
// before
let s = get_resource_value(...).await?; // fails if value is an object
// after
match get_value(...).await? {
Value::String(s) => Ok(s),
other => serde_json::from_value::<MyStruct>(other)
.context("resource is structured, not a string"),
} Defensive patterns
Strategy: type-guard
Validate before calling
// before calling the string getter, verify the stored shape
let stored: serde_json::Value = db
.query_opt("SELECT value FROM resource WHERE path = $1", &[&path])?
.ok_or_else(|| anyhow::anyhow!("resource {path} not found"))?
.get(0);
if !stored.is_string() {
return Err(anyhow::anyhow!("resource {path} is not a plain string"));
} Type guard
fn is_json_string(v: &serde_json::Value) -> bool {
matches!(v, serde_json::Value::String(_))
} Try / catch
match get_value_as_string(db, &workspace, &path).await {
Ok(s) => s,
Err(e) if e.to_string().starts_with("Expected string, got") => {
// value is structured: fetch as Value and extract/serialize
let v = get_value(db, &workspace, &path).await?;
serde_json::to_string(&v)?
}
Err(e) => return Err(e),
} Prevention
- Use the typed getter matching the stored shape (string getter only for strings)
- Audit resources/variables after schema or migration changes
- Inspect resource content in the UI before consuming it with string-typed APIs
- Standardize on storing plain strings where string getters are used downstream
When it happens
Trigger: Fetching a resource/variable whose stored value is not a JSON string — e.g. the caller stored an object or number, or the path resolves to a sub-value inside a structured resource while the API contract expects a plain string.
Common situations: Storing a JSON object as a resource then reading it with a string-typed getter; a resource path pointing at a nested field whose type changed; migration or manual DB edits changing value shape; using variable endpoints on resources and vice versa.
Related errors
- Completed jobs file must contain an array of jobs
- Failed to push completed jobs: ${e}
- Queued jobs file must contain an array of jobs
- Invalid JSON for ${field}: ${errorMessage}
- Invalid JSON after replacement: ${message}
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/088f7cc4e0ce8e2d.
Report an issue: GitHub.