siyuan-note/siyuan · error

previous key not found in current view: %s

Error message

previous key not found in current view: %s

What it means

After confirming prev is a string, databasePreviousKeyID checks it against the field IDs of the current view. If the supplied ID does not match any field in that view, the tool returns 'previous key not found in current view: %s', naming the offending value. The insertion position cannot be resolved without a matching field.

Source

Thrown at kernel/mcp/tools/database.go:348

	view, err := attrView.GetFirstView()
	if nil != err {
		return "", err
	}
	fieldIDs := databaseViewFieldIDs(view)
	if value, specified := args["prev"]; specified {
		prev, ok := value.(string)
		if !ok {
			return "", errors.New("prev must be a string")
		}
		if "" == prev {
			return "", nil
		}
		for _, fieldID := range fieldIDs {
			if fieldID == prev {
				return prev, nil
			}
		}
		return "", fmt.Errorf("previous key not found in current view: %s", prev)
	}
	if 0 < len(fieldIDs) {
		return fieldIDs[len(fieldIDs)-1], nil
	}
	return "", nil
}

func databaseViewFieldIDs(view *av.View) (ret []string) {
	if nil == view {
		return
	}
	switch view.LayoutType {
	case av.LayoutTypeTable:
		if nil != view.Table {
			for _, column := range view.Table.Columns {
				if nil != column && "" != column.ID {
					ret = append(ret, column.ID)
				}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Fetch the current view's field IDs and use an exact match; default behavior (omit prev) appends at the view end.
  2. Verify prev is a field ID (not the key's display name) and belongs to the same view.
  3. If the key was deleted, choose an existing sibling field as the anchor.
  4. Refresh your cached view metadata before retrying.

Example fix

// before
await mcp.call("databaseKeyAdd", { databaseID, name: "Notes", prev: "Old Key Name" });
// after
const fields = await listFieldIDs(databaseID);
await mcp.call("databaseKeyAdd", { databaseID, name: "Notes", prev: fields[fields.length - 1] });
Defensive patterns

Strategy: validation

Validate before calling

async function assertPrevInView(databaseID, prev) {
  const view = await getViewFields(databaseID);
  if (prev != null && !view.fieldIDs.includes(prev)) {
    throw new Error(`prev ${prev} not in view; valid: ${view.fieldIDs.join(',')}`);
  }
}

Try / catch

try {
  await mcp.call("databaseKeyAdd", { databaseID, name, prev });
} catch (e) {
  if (String(e.message).startsWith("previous key not found")) {
    // refresh field IDs and retry with a valid anchor or omit prev
  }
}

Prevention

When it happens

Trigger: Calling databaseKeyAdd with prev set to a field ID from a different view/table, a deleted key's ID, a key name instead of its field ID, or a fabricated/typo'd ID.

Common situations: Agents reusing IDs cached from another database; the referenced key having been removed between calls; passing display names instead of internal field IDs; view restructuring that changed field membership.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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