BigPizzaV3/CodexPlusPlus · error
model_metadata 的模型 值必须是对象
Error message
model_metadata 的模型 {slug} 值必须是对象 What it means
parse_model_metadata_map parses the model_metadata JSON string and requires both the top level and every per-model value to be a JSON object. If a slug maps to a non-object (string, number, array, bool, null), the apply fails so malformed model metadata is never written into the catalog/config.
Solutions
- Make every value in the model_metadata JSON a proper object, e.g. {"model-a": {"priority": 2}}
- Validate the JSON with jq or a linter before applying
- Remove the malformed slug entry if its metadata is unnecessary
- If migrating from an older format, wrap scalar values in an object with the appropriate field name
Example fix
// before
{"model-a": "enabled"}
// after
{"model-a": {"supports_search_tool": true}} Defensive patterns
Strategy: validation
Validate before calling
const map = JSON.parse(profile.model_metadata);
if (typeof map !== 'object' || map === null || Array.isArray(map)) throw new Error('model_metadata must be a JSON object');
for (const [slug, v] of Object.entries(map)) {
if (typeof v !== 'object' || v === null || Array.isArray(v)) throw new Error(`metadata for ${slug} must be an object`);
} Type guard
const isMetadataObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
Try / catch
try { applyProfile(profile); } catch (e) { if (String(e).includes('model_metadata')) { profile.model_metadata = sanitizeMetadata(profile.model_metadata); applyProfile(profile); } else throw e; } Prevention
- Always store per-model metadata as objects, even for a single field
- Lint model_metadata JSON before saving in the UI
- Migrate legacy scalar formats explicitly
When it happens
Trigger: Calling a config-apply path with profile.model_metadata set to JSON like {"model-a": "enabled"} or {"model-a": [1,2]} — any slug whose value is not a JSON object.
Common situations: Hand-editing model_metadata and using a string instead of an object for a model; an older config format stored per-model scalars; a copy/paste dropped the object braces around the metadata fields.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- must contain a JSON object
- 官方混合 API 不应在 auth.json 中保存 OPENAI_API_KEY。请清理此供应商的…
- 请填写 API 的模型名称
- auth.json 必须是 JSON 对象
- Base URL 不能为空
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@b1ed92e5e4 (2026-09-19).
Data as JSON: /api/errors/375c888f1cb1b547.
Report an issue: GitHub.
Appendix: source
Thrown at crates/codex-plus-core/src/relay_config.rs:2147
if crate::model_suffix::parse_compact_percent(value).is_none() {
anyhow::bail!("model_auto_compact 的模型 {slug} 百分比无效:{value}");
}
}
Ok(())
}
fn parse_model_metadata_map(metadata_json: &str) -> anyhow::Result<serde_json::Map<String, Value>> {
if metadata_json.trim().is_empty() {
return Ok(serde_json::Map::new());
}
let value: Value = serde_json::from_str(metadata_json)
.map_err(|error| anyhow::anyhow!("model_metadata JSON 解析失败:{error}"))?;
let map = value
.as_object()
.ok_or_else(|| anyhow::anyhow!("model_metadata 必须是 JSON 对象"))?;
for (slug, metadata) in map {
if !metadata.is_object() {
anyhow::bail!("model_metadata 的模型 {slug} 值必须是对象");
}
}
Ok(map.clone())
}
fn model_metadata_has_entries(
metadata: &serde_json::Map<String, Value>,
entry_slugs: &HashSet<String>,
) -> bool {
metadata.keys().any(|slug| entry_slugs.contains(slug))
}
fn apply_model_metadata_overrides(
catalog_json: &str,
override_map: &serde_json::Map<String, Value>,
) -> anyhow::Result<String> {
if override_map.is_empty() {
return Ok(catalog_json.to_string());View on GitHub (pinned to b1ed92e5e4)