{"record":{"id":"16a1f7f8d05925ea","repo":"m1k1o/neko","slug":"unknown-resource-s","errorCode":null,"errorMessage":"unknown resource: %s","messagePattern":"unknown resource: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/http/legacy/wstobackend.go","lineNumber":289,"sourceCode":"\t\trequest := &oldMessage.AdminLock{}\n\t\terr := json.Unmarshal(msg, request)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdata := map[string]any{}\n\n\t\tswitch request.Resource {\n\t\tcase \"login\":\n\t\t\tdata[\"locked_logins\"] = true\n\t\tcase \"control\":\n\t\t\tdata[\"locked_controls\"] = true\n\t\tcase \"file_transfer\":\n\t\t\tdata[\"plugins\"] = map[string]any{\n\t\t\t\t\"filetransfer.enabled\": false,\n\t\t\t}\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"unknown resource: %s\", request.Resource)\n\t\t}\n\n\t\treturn s.apiReq(http.MethodPost, \"/api/room/settings\", data, nil)\n\n\tcase oldEvent.ADMIN_UNLOCK:\n\t\trequest := &oldMessage.AdminLock{}\n\t\terr := json.Unmarshal(msg, request)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdata := map[string]any{}\n\n\t\tswitch request.Resource {\n\t\tcase \"login\":\n\t\t\tdata[\"locked_logins\"] = false\n\t\tcase \"control\":\n\t\t\tdata[\"locked_controls\"] = false","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/m1k1o/neko/blob/b0f01cedea68893e85a3fd852c0521238c285695/server/internal/http/legacy/wstobackend.go#L271-L307","documentation":"In the legacy websocket-to-backend translation layer, the ADMIN_LOCK event carries a `resource` field that must be one of a fixed set of values the shim knows how to map onto new backend room settings. When `request.Resource` matches none of the handled cases ('login', 'control', 'file_transfer'), wsToBackend returns fmt.Errorf(\"unknown resource: %s\", request.Resource) instead of issuing the /api/room/settings call.","triggerScenarios":"A legacy client sends an admin lock websocket message whose `resource` field is a string other than \"login\", \"control\", or \"file_transfer\" — e.g. a typo like \"filetransfer\", an empty string, or a resource added in the old protocol after this shim was written.","commonSituations":"Running an old frontline/admin client against a newer backend through the legacy shim; hand-crafted websocket messages from scripts or tests; version drift where the old protocol gained a new lockable resource that the shim's switch statement was never extended to translate.","solutions":["Log the received request.Resource value and confirm which string the legacy client is sending","Map the value to one of the supported resources ('login', 'control', 'file_transfer') on the client side, or fix the client typo","Extend the switch in wsToBackend ADMIN_LOCK handling with a new case that translates the resource into the corresponding /api/room/settings key","Update the legacy shim and clients to the same protocol version"],"exampleFix":"// before (client payload)\n{\"event\":\"admin_lock\",\"resource\":\"filetransfer\"}\n// after\n{\"event\":\"admin_lock\",\"resource\":\"file_transfer\"}","handlingStrategy":"validation","validationCode":"func validateLockResource(resource string) error {\n\tswitch resource {\n\tcase \"login\", \"control\", \"file_transfer\":\n\t\treturn nil\n\tdefault:\n\t\treturn fmt.Errorf(\"unsupported lock resource %q: must be login, control, or file_transfer\", resource)\n\t}\n}","typeGuard":"func isKnownLockResource(r string) bool {\n\tswitch r {\n\tcase \"login\", \"control\", \"file_transfer\":\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"if err := validateLockResource(req.Resource); err != nil {\n\tlog.Printf(\"skipping admin_lock: %v\", err)\n\treturn nil // or surface a client-facing 'unsupported resource' message\n}","preventionTips":["Validate the resource string on the client before sending admin_lock frames","Keep a shared constant list of supported resources between client and shim","Add a case or explicit rejection in tests for every resource the old protocol defines"],"tags":["websocket","legacy-protocol","validation"],"backgroundTag":"invalid-enum-value","analyzedSha":"b0f01cedea68893e85a3fd852c0521238c285695","analyzedAt":"2026-09-01T10:35:56.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}