m1k1o/neko · error

unknown resource: %s

Error message

unknown resource: %s

What it means

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.

Source

Thrown at server/internal/http/legacy/wstobackend.go:289

		request := &oldMessage.AdminLock{}
		err := json.Unmarshal(msg, request)
		if err != nil {
			return err
		}

		data := map[string]any{}

		switch request.Resource {
		case "login":
			data["locked_logins"] = true
		case "control":
			data["locked_controls"] = true
		case "file_transfer":
			data["plugins"] = map[string]any{
				"filetransfer.enabled": false,
			}
		default:
			return fmt.Errorf("unknown resource: %s", request.Resource)
		}

		return s.apiReq(http.MethodPost, "/api/room/settings", data, nil)

	case oldEvent.ADMIN_UNLOCK:
		request := &oldMessage.AdminLock{}
		err := json.Unmarshal(msg, request)
		if err != nil {
			return err
		}

		data := map[string]any{}

		switch request.Resource {
		case "login":
			data["locked_logins"] = false
		case "control":
			data["locked_controls"] = false

View on GitHub (pinned to b0f01cedea)

Solutions

  1. Log the received request.Resource value and confirm which string the legacy client is sending
  2. Map the value to one of the supported resources ('login', 'control', 'file_transfer') on the client side, or fix the client typo
  3. Extend the switch in wsToBackend ADMIN_LOCK handling with a new case that translates the resource into the corresponding /api/room/settings key
  4. Update the legacy shim and clients to the same protocol version

Example fix

// before (client payload)
{"event":"admin_lock","resource":"filetransfer"}
// after
{"event":"admin_lock","resource":"file_transfer"}
Defensive patterns

Strategy: validation

Validate before calling

func validateLockResource(resource string) error {
	switch resource {
	case "login", "control", "file_transfer":
		return nil
	default:
		return fmt.Errorf("unsupported lock resource %q: must be login, control, or file_transfer", resource)
	}
}

Type guard

func isKnownLockResource(r string) bool {
	switch r {
	case "login", "control", "file_transfer":
		return true
	}
	return false
}

Try / catch

if err := validateLockResource(req.Resource); err != nil {
	log.Printf("skipping admin_lock: %v", err)
	return nil // or surface a client-facing 'unsupported resource' message
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of m1k1o/neko@b0f01cedea (2026-09-01). Data as JSON: /api/errors/16a1f7f8d05925ea. Report an issue: GitHub.