{"record":{"id":"0b65500fa512be34","repo":"siyuan-note/siyuan","slug":"previous-key-not-found-in-current-view-s","errorCode":null,"errorMessage":"previous key not found in current view: %s","messagePattern":"previous key not found in current view: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/database.go","lineNumber":348,"sourceCode":"\tview, err := attrView.GetFirstView()\n\tif nil != err {\n\t\treturn \"\", err\n\t}\n\tfieldIDs := databaseViewFieldIDs(view)\n\tif value, specified := args[\"prev\"]; specified {\n\t\tprev, ok := value.(string)\n\t\tif !ok {\n\t\t\treturn \"\", errors.New(\"prev must be a string\")\n\t\t}\n\t\tif \"\" == prev {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tfor _, fieldID := range fieldIDs {\n\t\t\tif fieldID == prev {\n\t\t\t\treturn prev, nil\n\t\t\t}\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"previous key not found in current view: %s\", prev)\n\t}\n\tif 0 < len(fieldIDs) {\n\t\treturn fieldIDs[len(fieldIDs)-1], nil\n\t}\n\treturn \"\", nil\n}\n\nfunc databaseViewFieldIDs(view *av.View) (ret []string) {\n\tif nil == view {\n\t\treturn\n\t}\n\tswitch view.LayoutType {\n\tcase av.LayoutTypeTable:\n\t\tif nil != view.Table {\n\t\t\tfor _, column := range view.Table.Columns {\n\t\t\t\tif nil != column && \"\" != column.ID {\n\t\t\t\t\tret = append(ret, column.ID)\n\t\t\t\t}","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/tools/database.go#L330-L366","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait mcp.call(\"databaseKeyAdd\", { databaseID, name: \"Notes\", prev: \"Old Key Name\" });\n// after\nconst fields = await listFieldIDs(databaseID);\nawait mcp.call(\"databaseKeyAdd\", { databaseID, name: \"Notes\", prev: fields[fields.length - 1] });","handlingStrategy":"validation","validationCode":"async function assertPrevInView(databaseID, prev) {\n  const view = await getViewFields(databaseID);\n  if (prev != null && !view.fieldIDs.includes(prev)) {\n    throw new Error(`prev ${prev} not in view; valid: ${view.fieldIDs.join(',')}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await mcp.call(\"databaseKeyAdd\", { databaseID, name, prev });\n} catch (e) {\n  if (String(e.message).startsWith(\"previous key not found\")) {\n    // refresh field IDs and retry with a valid anchor or omit prev\n  }\n}","preventionTips":["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"],"tags":["mcp","database","not-found"],"backgroundTag":"resource-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}