Tencent/WeKnora · warning
rbac: resource not found
Error message
rbac: resource not found
What it means
ErrResourceNotFound is the sentinel returned by RBAC CreatorLookups when the targeted resource row does not exist or belongs to a different tenant. RequireOwnershipOrRole deliberately lets requests carrying this error proceed so the downstream handler can return its own 404 — a middleware 403 would mask genuine 'wrong URL' failures. Note the similarly-named datasource.ErrResourceNotFound ('resource not found in source system') used by connectors like Notion on HTTP 404; they are distinct sentinels.
Source
Thrown at internal/middleware/rbac.go:23
"errors"
"net/http"
"sync"
"github.com/Tencent/WeKnora/internal/config"
"github.com/Tencent/WeKnora/internal/logger"
"github.com/Tencent/WeKnora/internal/types"
"github.com/gin-gonic/gin"
)
// ErrResourceNotFound is the sentinel a CreatorLookup returns when the
// :id on the request does not match any row the lookup can see (either
// the row is genuinely missing or its tenant doesn't match). When a
// lookup returns this error, RequireOwnershipOrRole intentionally lets
// the request proceed so the downstream handler can respond with its
// own 404 — middleware-level 403 would hide real "URL is wrong" failures
// behind a permissions error, which breaks client diagnostics and
// operator dashboards.
var ErrResourceNotFound = errors.New("rbac: resource not found")
// CreatorLookup resolves the creator user ID for the resource targeted
// by the current request, based on whatever is on the gin.Context (URL
// params, query, body). Implementations live next to the handlers they
// guard, e.g. handler.kbCreatorLookup(c) reads ":id" and returns
// KnowledgeBase.CreatorID.
//
// Return value contract:
// - (creatorID, nil) where creatorID != "" -> the resource has a
// recorded owner; ownership match grants access.
// - ("", nil) -> "tenant-owned": no
// human creator was recorded (legacy row or built-in resource);
// only callers whose role meets the bar may proceed.
// - ("", ErrResourceNotFound) -> the :id does not
// resolve to any row visible to this caller's tenant. Middleware
// proceeds to the handler so the handler can return 404 instead
// of masking it as 403.
// - ("", other error) -> transient orView on GitHub (pinned to 988cbb0330)
Solutions
- Verify the resource ID in the request URL is correct and still exists (query the DB or list endpoint).
- Confirm the request is made against the correct tenant/workspace — cross-tenant lookups produce the same sentinel.
- If deleted resources should 404 explicitly, ensure the downstream handler maps this case to a NotFoundError response (it will, since middleware lets it through).
- Use errors.Is(err, middleware.ErrResourceNotFound) to distinguish not-found from ErrOwnershipForbidden when handling.
Example fix
// before: treating any eval error as forbidden
if evalErr != nil {
return errors.NewForbiddenError("no permission")
}
// after: distinguish not-found from forbidden
if goerrors.Is(evalErr, middleware.ErrResourceNotFound) {
return errors.NewNotFoundError("knowledge base not found")
}
if goerrors.Is(evalErr, middleware.ErrOwnershipForbidden) {
return errors.NewForbiddenError("No permission to operate on this knowledge base")
} Defensive patterns
Strategy: type-guard
Validate before calling
// before mutating, confirm the resource exists in your tenant
kb, err := client.GetKnowledgeBase(ctx, id)
if errors.Is(err, ErrNotFound) || kb == nil {
return fmt.Errorf("knowledge base %s does not exist in this tenant", id)
} Type guard
func isRBACNotFound(err error) bool {
return errors.Is(err, middleware.ErrResourceNotFound)
}
// note: do not confuse with datasource.ErrResourceNotFound (connector 404s) Try / catch
if err != nil {
switch {
case errors.Is(err, middleware.ErrResourceNotFound):
// let the handler 404 — the URL/resource itself is wrong
return errors.NewNotFoundError("not found")
case errors.Is(err, middleware.ErrOwnershipForbidden):
return errors.NewForbiddenError("no permission")
}
return err
} Prevention
- Always match sentinels with errors.Is, not string comparison.
- Distinguish middleware.ErrResourceNotFound from datasource.ErrResourceNotFound.
- Treat this error as a client-side wrong-ID/tenant problem, not a permissions problem.
- Purge cached links after deleting resources.
When it happens
Trigger: A CreatorLookup (KBCreatorLookup, KBCreatorLookupFromKbIDParam, AgentCreatorLookup) queries the DB for the resource named by the URL param and finds no row (nonexistent ID) or a row with a mismatched tenant ID. Also returned by doRequest on 404 paths.
Common situations: Client requests a deleted or never-existing knowledge base/agent ID; ID from another tenant; stale links cached after resource deletion; Notion connector fetching a page that was deleted in the source system (datasource variant).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- rbac: ownership or role insufficient
- join request not found
- knowledge service returned an empty result
- wiki page %s returned knowledge base %s while resolving allo
- %w: %s
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/1cd896ee24f7c9d5.
Report an issue: GitHub.