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

  1. Send each key as an object with at least name and type fields: [{"name":"Title","type":"block"}].
  2. Review the full schema — required fields per key are name, type, and optionally icon.
  3. If you only have key names, decide the appropriate type for each before calling.
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/745e8b95c6409cb0. Report an issue: GitHub.