siyuan-note/siyuan · error
each key must be an object
Error message
each key must be an object
What it means
Within databaseCreateKeySpecs, each element of the keys array must itself be a JSON object (map[string]any) describing a key. If any element is a scalar, string, or nested array, the tool returns 'each key must be an object' and aborts the database creation.
Source
Thrown at kernel/mcp/tools/database.go:203
"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 8641553a1f)
Solutions
- Send each key as an object with at least name and type fields: [{"name":"Title","type":"block"}].
- Review the full schema — required fields per key are name, type, and optionally icon.
- If you only have key names, decide the appropriate type for each before calling.
- Validate the payload shape in the client (array of plain objects) before invoking.
Example fix
// before
{ "keys": ["Title", "Date"] }
// after
{ "keys": [ { "name": "Title", "type": "block" }, { "name": "Date", "type": "date" } ] } Defensive patterns
Strategy: type-guard
Validate before calling
function assertKeySpecs(keys) {
return keys.map(k => {
if (typeof k !== 'object' || k === null || Array.isArray(k)) {
throw new TypeError('each key must be an object, got: ' + JSON.stringify(k));
}
return k;
});
} Type guard
function isKeySpec(k) { return typeof k === 'object' && k !== null && !Array.isArray(k); } Try / catch
try {
await mcp.call("databaseCreate", { name, keys });
} catch (e) {
if (e.message === "each key must be an object") {
// convert ["Title"] into [{ name: "Title", type: ... }]
}
} Prevention
- Represent key specs as objects with name/type from the start
- Never send raw key-name strings or nested arrays
- Unit-test the payload builder against the documented schema
When it happens
Trigger: Calling databaseCreate with keys like ["Title", "Date"] or [["name","Title"]] instead of an array of objects [{"name":"Title",...}].
Common situations: Agents generating a list of key names instead of full key specs; clients converting objects into arrays of arrays; template placeholders expanded into plain strings.
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
- keys must be an array
- each key requires name and type
- --av is required
- prev must be a string
- previous key not found in current view: %s
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/745e8b95c6409cb0.
Report an issue: GitHub.