siyuan-note/siyuan · error
each key must be an object
Error message
each key must be an object
What it means
While iterating the keys array in databaseCreateKeySpecs (kernel/mcp/tools/database.go), each element must itself be a JSON object describing one attribute-view key (name, type, optional icon). A non-object element (string, number, nested array) is rejected with this error.
Solutions
- Wrap each key spec as an object with at least name and type fields
- Check each array element is map/object-shaped before the call
- Refer to the database create tool schema for the exact key object fields
- Add client-side schema validation (e.g. JSON Schema) for the keys argument
Example fix
// before
keys: ["Title", "Date"]
// after
keys: [{name: "Title", type: "text"}, {name: "Date", type: "date"}] Defensive patterns
Strategy: validation
Validate before calling
const ok = keys.every(k => k !== null && typeof k === "object" && !Array.isArray(k));
Type guard
const isKeySpec = (k) => typeof k === "object" && k !== null && !Array.isArray(k);
Try / catch
try { await call("database_create", {keys}) } catch (e) { if (String(e).includes("each key must be an object")) { console.error("non-object key element:", keys.find(k => typeof k !== "object")); } throw e; } Prevention
- Use {name, type} objects, not strings or tuples
- Validate each element client-side
- Avoid LLM shorthand formats
When it happens
Trigger: Calling database create with keys like ["Title"] or [["Title","text"]] instead of [{"name":"Title","type":"text"}].
Common situations: Clients using a shorthand list of key names; LLM-generated arguments using tuples instead of objects; copy-pasted config where objects were flattened.
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
- each key requires name and type
- keys must be an array
- prev must be a string
- attr must be a string or null (got %T)
- --av and --ids are required
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/745e8b95c6409cb0.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/mcp/tools/database.go:211
"blockID": result.BlockID,
"avID": result.AvID,
"viewID": result.ViewID,
"database": model.NewAttributeViewMetadata(result.AttributeView),
})
}
func databaseCreateKeySpecs(value any) (ret []*model.AttributeViewCreateKey, err error) {
if nil == value {
return []*model.AttributeViewCreateKey{}, nil
}
items, ok := value.([]any)
if !ok {
return nil, errors.New("keys must be an array")
}
for _, item := range items {
data, itemOK := item.(map[string]any)
if !itemOK {
return nil, errors.New("each key must be an object")
}
name, _ := data["name"].(string)
keyType, _ := data["type"].(string)
icon, _ := data["icon"].(string)
if "" == strings.TrimSpace(name) || "" == strings.TrimSpace(keyType) {
return nil, errors.New("each key requires name and type")
}
ret = append(ret, &model.AttributeViewCreateKey{Name: name, Type: keyType, Icon: icon})
}
return
}
func databaseSuccess(action string, data any) (CallToolResult, error) {
output := &databaseToolOutput{Action: action, Data: data}
serialized, err := json.Marshal(output)
if nil != err {
return CallToolResult{}, err
}View on GitHub (pinned to 9f775e8a12)