{"record":{"id":"69f1748960445e90","repo":"siyuan-note/siyuan","slug":"invalid-pinned-document-action","errorCode":null,"errorMessage":"invalid pinned document action","messagePattern":"invalid pinned document action","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/pinned_docs.go","lineNumber":163,"sourceCode":"\t\t\tdoc.SubFileCount = BoxDocSubFileCount(boxID)\n\t\t} else {\n\t\t\tdoc.SubFileCount, err = visibleDocCount(boxID, strings.TrimSuffix(bt.Path, \".sy\"), box.docIAL, nil)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tret = append(ret, doc)\n\t}\n\treturn\n}\n\n// 根层顺序独立于源文档顺序，按相对位置更新以保留其他窗口新增的入口。\nfunc UpdatePinnedDocs(ids []string, action, targetID string, after bool) error {\n\tif len(ids) == 0 {\n\t\treturn fmt.Errorf(\"document IDs are required\")\n\t}\n\tif action != \"pin\" && action != \"unpin\" {\n\t\treturn fmt.Errorf(\"invalid pinned document action\")\n\t}\n\tpinnedDocsLock.Lock()\n\tdefer pinnedDocsLock.Unlock()\n\tstored, err := readPinnedDocs()\n\tif err != nil {\n\t\treturn err\n\t}\n\tselected := map[string]bool{}\n\trefs := []pinnedDocRef{}\n\tfor _, id := range ids {\n\t\tif !ast.IsNodeIDPattern(id) {\n\t\t\treturn fmt.Errorf(\"invalid document ID [%s]\", id)\n\t\t}\n\t\tif selected[id] {\n\t\t\tcontinue\n\t\t}\n\t\tselected[id] = true\n\t\tif action == \"unpin\" {","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/pinned_docs.go#L145-L181","documentation":"Validation guard in UpdatePinnedDocs: fires when the action argument is anything other than \"pin\" or \"unpin\". The action selects whether documents are added to or removed from the pinned root layer; a misspelled or unsupported action string is the faulting input, caught before the pinnedDocsLock is taken.","triggerScenarios":"Calling the pinned-docs update API with action set to any value other than \"pin\"/\"unpin\" — e.g. typo (\"pinned\"), localized string, uppercase (\"PIN\"), or null/missing parameter.","commonSituations":"Plugin or script passing a wrong/hardcoded action string; API version mismatch where an older client uses a removed action name; copy-paste errors in automation scripts.","solutions":["Pass exactly \"pin\" or \"unpin\" (lowercase) as the action parameter","Check the client code/plugin for typos or case mismatches and normalize the value","Consult the current API docs for supported action values if migrating an older integration"],"exampleFix":"// before\nawait fetchPost(\"/api/filetree/updatePinnedDocs\", {ids, action: \"Pin\"});\n// after\nconst action = wantPin ? \"pin\" : \"unpin\";\nawait fetchPost(\"/api/filetree/updatePinnedDocs\", {ids, action});","handlingStrategy":"validation","validationCode":"// Caller-side enum check before invoking the update API\nif action != \"pin\" && action != \"unpin\" {\n\treturn fmt.Errorf(\"action must be pin or unpin, got %q\", action)\n}","typeGuard":"func isValidPinnedAction(s string) bool { return s == \"pin\" || s == \"unpin\" }","tryCatchPattern":"err := UpdatePinnedDocs(ids, action, targetID, after)\nif err != nil && strings.Contains(err.Error(), \"invalid pinned document action\") {\n\t// fix the action constant at the call site\n}","preventionTips":["Define pin/unpin action strings as named constants shared by caller and API","Never pass user-supplied or localized strings as the action value","Keep API clients in sync with the current endpoint contract","Add unit tests covering both valid actions and a rejection case"],"tags":["go","api","validation","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}