{"record":{"id":"edcef24287c3b2ed","repo":"siyuan-note/siyuan","slug":"invalid-frontend-capability-id-s","errorCode":null,"errorMessage":"invalid frontend capability ID: %s","messagePattern":"invalid frontend capability ID: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/agent/capability.go","lineNumber":216,"sourceCode":"\t\t\tDescription:   tool.Description,\n\t\t\tSource:        source,\n\t\t\tOwnerID:       tool.OwnerID,\n\t\t\tOwnerName:     tool.OwnerName,\n\t\t\tRuntime:       runtime,\n\t\t\tTool:          tool,\n\t\t\tValidator:     validator,\n\t\t\tInputSchema:   tool.InputSchema,\n\t\t\tOutputSchema:  tool.OutputSchema,\n\t\t\tAccessContext: accessContext,\n\t\t}\n\t\tif err := set.add(registration); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tfor _, frontend := range frontendCapabilities {\n\t\tif !validFrontendCapabilityID(frontend.ID) {\n\t\t\treturn nil, fmt.Errorf(\"invalid frontend capability ID: %s\", frontend.ID)\n\t\t}\n\t\tif strings.TrimSpace(frontend.Description) == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"frontend capability description is required: %s\", frontend.ID)\n\t\t}\n\t\tif !capabilityAllowed(frontend.ID, accessContext) {\n\t\t\tcontinue\n\t\t}\n\t\tvalidationTool := &tools.Tool{\n\t\t\tName:         frontendCapabilityModelName(frontend.ID),\n\t\t\tDescription:  frontend.Description,\n\t\t\tInputSchema:  frontend.InputSchema,\n\t\t\tOutputSchema: frontend.OutputSchema,\n\t\t}\n\t\tvalidator, err := tools.CompileToolValidator(validationTool)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid frontend capability [%s]: %w\", frontend.ID, err)\n\t\t}\n\t\tsource := \"native\"","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/agent/capability.go#L198-L234","documentation":"capability.go validates each frontend capability id with validFrontendCapabilityID(). The id must be exactly 'native/frontend/<name>' (3 segments) or 'plugin/frontend/<owner>/<name>' (4 segments), with no empty segments and total length <= 512. Any other shape returns the formatted error naming the offending id.","triggerScenarios":"A frontend capability is registered with an id missing a segment (e.g. 'plugin/frontend/' with empty owner or name), using a wrong prefix ('frontend/x/y'), extra slashes, a >512 char id, or a name that URI-decodes to empty.","commonSituations":"Plugin uses encodeURIComponent on an already-encoded name producing extra slashes; capability id constructed from user input that was blank; refactor changed the prefix constant; URIDecode on the server yields an empty trailing segment.","solutions":["Format ids strictly as 'native/frontend/<name>' or 'plugin/frontend/<owner>/<name>' with non-empty owner/name.","Validate the id client-side with the same segment rule before sending it to the kernel.","Trim owner/name and reject empty values at registration time in the frontend (Plugin.addAgentCapability already does encodeURIComponent — supply non-empty raw values).","Keep the total id length under 512 characters."],"exampleFix":"// before\nid := fmt.Sprintf('plugin/frontend/%s/', owner) // empty name segment\n// after\nif owner == '' || name == '' { return errors.New('owner and name required') }\nid := fmt.Sprintf('plugin/frontend/%s/%s', owner, name)","handlingStrategy":"validation","validationCode":"func validID(id string) bool {\n    if len(id) > 512 { return false }\n    p := strings.Split(id, '/')\n    return (len(p) == 3 && p[0]=='native' && p[1]=='frontend' && p[2] != '') ||\n        (len(p) == 4 && p[0]=='plugin' && p[1]=='frontend' && p[2] != '' && p[3] != '')\n}","typeGuard":null,"tryCatchPattern":"if err := buildCapabilitySet(...); err != nil {\n    if strings.Contains(err.Error(), 'invalid frontend capability ID') { /* log offending id, skip */ }\n    return err\n}","preventionTips":["Always format ids as 'native/frontend/<name>' or 'plugin/frontend/<owner>/<name>'.","Trim and reject empty owner/name before constructing the id.","Keep total id length under 512 characters.","Share the id-format validator between frontend and kernel."],"tags":["agent","capability","validation","go"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}