cube-js/cube · error
--data must be a JSON object
Error message
--data must be a JSON object
What it means
parse_data normalizes the --data flag value (string or already-parsed) into a serde_json Map. After deserializing the raw text as JSON (which would first fail with "--data is not valid JSON" if malformed), it requires the top-level value to be a JSON object. Scalars, arrays, or other non-object values are rejected because every request body the CLI builds is an object.
Source
Thrown at rust/cube-cli/src/util.rs:28
/// Accepts inline JSON (`'{"name": "x"}'`), `@path/to/file.json`, or `-`
/// to read from stdin — the same convention as `gh api` / `curl -d`.
pub fn parse_data(data: Option<&str>) -> Result<Map<String, Value>> {
let Some(data) = data else {
return Ok(Map::new());
};
let raw = if data == "-" {
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
buf
} else if let Some(path) = data.strip_prefix('@') {
std::fs::read_to_string(path).with_context(|| format!("failed to read {path}"))?
} else {
data.to_string()
};
let value: Value = serde_json::from_str(&raw).context("--data is not valid JSON")?;
match value {
Value::Object(map) => Ok(map),
_ => bail!("--data must be a JSON object"),
}
}
/// Insert a flag value into a JSON body if it was provided on the CLI.
pub fn set<T: serde::Serialize>(body: &mut Map<String, Value>, key: &str, value: &Option<T>) {
if let Some(v) = value {
body.insert(key.to_string(), serde_json::to_value(v).unwrap());
}
}
/// Push a query parameter if the flag was provided.
pub fn push<T: ToString>(query: &mut Query, key: &str, value: &Option<T>) {
if let Some(v) = value {
query.push((key.to_string(), v.to_string()));
}
}
/// How one endpoint implements the deprecated offset paging it still accepts,View on GitHub (pinned to 7d981676b3)
Solutions
- Wrap the value in a JSON object, e.g. --data '{"key": "value"}'
- If you meant to send a list, find the field it belongs under: --data '{"records": [...]}'
- Check shell quoting — the value must arrive as one argument containing valid JSON
Example fix
// before
cube load --data '[{"id":1},{"id":2}]'
// after
cube load --data '{"records": [{"id":1},{"id":2}]}' Defensive patterns
Strategy: validation
Validate before calling
// validate --data before invoking
class IsObject {}
function isJsonObject(s) {
try { const v = JSON.parse(s); return v !== null && typeof v === 'object' && !Array.isArray(v); }
catch { return false; }
}
if (!isJsonObject(dataArg)) throw new Error('--data must be a JSON object'); Type guard
function isJsonObject(v) {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Try / catch
match parse_data(&data_arg) {
Err(e) if e.to_string().contains("--data must be a JSON object") => {
eprintln!("Pass a top-level JSON object, e.g. --data '{{\"key\": \"value\"}}'");
}
Err(e) => return Err(e),
Ok(map) => { /* use map */ }
} Prevention
- Always pass --data a JSON object literal with quoted keys
- Wrap arrays under an object field instead of sending bare arrays
- Single-quote the value in shells to avoid word-splitting/scalar coercion
When it happens
Trigger: Passing --data a top-level JSON array (`'[1,2]'`), a scalar (`'42'`, `'"str"'`), or any non-object value. Valid inline JSON that is not an object triggers exactly this bail.
Common situations: Users passing a JSON array of records intending bulk upload; quoting issues causing the shell to hand the flag a scalar; copy-pasted payloads whose root is an array.
Related errors
- {} are deprecated and cannot be combined with --first/--afte
- Invalid JSON payload
- API URL is empty
- provide --file <path> or --content <text>
- {} is not a directory
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/c70e1a6626655669.
Report an issue: GitHub.