{"record":{"id":"b7409775d88fae7d","repo":"siyuan-note/siyuan","slug":"agent-runtime-user-entry-not-found","errorCode":null,"errorMessage":"agent runtime user entry not found","messagePattern":"agent runtime user entry not found","errorType":"http","errorClass":null,"httpStatus":200,"severity":"error","filePath":"kernel/agent/runtime.go","lineNumber":456,"sourceCode":"\t\tif entry[\"type\"] != \"user\" {\n\t\t\tcontinue\n\t\t}\n\t\tid, _ := entry[\"id\"].(string)\n\t\tif userEntryID == \"\" || id == userEntryID {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n\nfunc applyRuntimeTurnToSessionLocked(session map[string]any, turn *agentRuntimeTurn) error {\n\tif turn == nil {\n\t\treturn nil\n\t}\n\tentries, _ := session[\"entries\"].([]any)\n\tanchor := findRuntimeUserAnchor(session, turn.UserEntryID)\n\tif anchor < 0 {\n\t\treturn fmt.Errorf(\"agent runtime user entry not found\")\n\t}\n\tif turn.Mode == \"regenerate\" && turn.UserContent != \"\" {\n\t\tentry, _ := entries[anchor].(map[string]any)\n\t\tentry[\"content\"] = turn.UserContent\n\t\tif turn.UserBlockHTML != nil {\n\t\t\tif *turn.UserBlockHTML != \"\" {\n\t\t\t\tentry[\"blockHTML\"] = *turn.UserBlockHTML\n\t\t\t} else {\n\t\t\t\tdelete(entry, \"blockHTML\")\n\t\t\t}\n\t\t}\n\t\tif turn.UserReferences != nil {\n\t\t\tif len(*turn.UserReferences) > 0 {\n\t\t\t\tentry[\"references\"] = *turn.UserReferences\n\t\t\t} else {\n\t\t\t\tdelete(entry, \"references\")\n\t\t\t}\n\t\t}","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/agent/runtime.go#L438-L474","documentation":"Rejected by siyuan.storage.watcher.add(path) when no argument is supplied or the first argument is not a string (kernel/plugin/api_storage.go:59). The watcher API needs a concrete path (or relative key) inside the plugin storage to subscribe to; missing, undefined, null, or non-string values are rejected before path resolution runs, so unlike get/put it cannot even attempt an empty path.","triggerScenarios":"watcher.add() with no argument; watcher.add(undefined) from an unset config key; watcher.add(5) or watcher.add({path: 'x'}) passing the wrong type.","commonSituations":"Optional watch-path settings that default to undefined; refactors extracting the path into a variable that is only conditionally assigned; passing the whole settings object instead of the field.","solutions":["Pass a string path: await siyuan.storage.watcher.add('notes')","Skip the call when the configured path is missing rather than calling with undefined","Validate the config at plugin load and fall back to a default watch path"],"exampleFix":"// before\nawait siyuan.storage.watcher.add(cfg.watchPath); // undefined when unset\n\n// after\nif (typeof cfg.watchPath === 'string' && cfg.watchPath) {\n  await siyuan.storage.watcher.add(cfg.watchPath);\n}","handlingStrategy":"type-guard","validationCode":"if (typeof cfg.watchPath === 'string' && cfg.watchPath.length > 0) {\n  await siyuan.storage.watcher.add(cfg.watchPath);\n}","typeGuard":"const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;","tryCatchPattern":"try { await siyuan.storage.watcher.add(path); } catch (e) { if (/path required/.test(e.message)) console.error('watch path missing from settings'); else throw e; }","preventionTips":["Validate watch-path settings at plugin load and apply a default","Skip watcher.add when the path is unset instead of calling with undefined","Pass the string field itself, never the whole settings object"],"tags":["storage","plugin-api","watcher","argument-validation","siyuan"],"backgroundTag":"missing-required-argument","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}