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
- Fetch the current view's field IDs and use an exact match; default behavior (omit prev) appends at the view end.
- Verify prev is a field ID (not the key's display name) and belongs to the same view.
- If the key was deleted, choose an existing sibling field as the anchor.
- 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
- Fetch field IDs from the same view immediately before inserting
- Never reuse IDs from other views or removed keys
- Use key display names only for lookup, field IDs for the prev argument
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
- attribute view not found
- view not found
- database not found: %s
- keys must be an array
- each key must be an object
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/0b65500fa512be34.
Report an issue: GitHub.