Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Kubernetes & Helm By James Joyner IV · · 9 min read

Kubernetes Error Guide: 'cannot list resource' RBAC Forbidden Failures

Fix Kubernetes RBAC 'Forbidden: cannot list resource' errors: missing RoleBindings, wrong namespace or subject, cluster-scoped resources, and ServiceAccount tokens.

  • #kubernetes
  • #troubleshooting
  • #errors
  • #rbac

Exact Error Message

A human user lacking permission to list pods sees:

Error from server (Forbidden): pods is forbidden: User "jane@example.com" cannot list resource "pods" in API group "" in the namespace "default"

The same error from a ServiceAccount names the subject as system:serviceaccount:<namespace>:<name>:

Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:ci:deployer" cannot list resource "pods" in API group "" in the namespace "staging"

Cluster-scoped resources report no namespace at all:

Error from server (Forbidden): nodes is forbidden: User "jane@example.com" cannot list resource "nodes" in API group "" at the cluster scope

What the Error Means

This is a 403 Forbidden from the Kubernetes authorization layer. The request was authenticated — the API server knows who you are — but not authorized: no RBAC rule grants the identity permission to perform the verb (list) on the resource (pods) in the scope (namespace "default"). This is fundamentally different from a 401 Unauthorized, where the API server could not identify you at all.

Every part of the message is a clue. User "..." is the subject RBAC evaluated. cannot list is the verb. resource "pods" and API group "" (the empty string is the core group) identify exactly what a rule must allow. in the namespace "default" versus at the cluster scope tells you whether a namespaced Role/RoleBinding or a cluster-wide ClusterRole/ClusterRoleBinding is required. RBAC is purely additive: with no matching allow rule, access is denied by default.

Common Causes

  • No Role/ClusterRole binding at all for the subject — the most common case, especially for a freshly created ServiceAccount.
  • RoleBinding in the wrong namespace. A RoleBinding only grants access in its own namespace; a binding in default does nothing for requests against staging.
  • Cluster-scoped resource bound with a namespaced Role. Listing nodes, namespaces, or persistentvolumes requires a ClusterRole + ClusterRoleBinding; a Role can never grant cluster-scoped access.
  • Wrong subject name or kind. A typo in the user/SA name, or kind: User where it should be kind: ServiceAccount (or a missing namespace on the SA subject), means the binding matches nothing.
  • Wrong apiGroup or resource in the rule. Putting pods under the wrong group, or using deployment instead of deployments, leaves the real request uncovered.
  • Using a ServiceAccount token without any binding. A pod or CI job authenticates as its SA but no RoleBinding references that SA.
  • Aggregated ClusterRoles. A custom rule was added expecting it to be picked up by an aggregated role (like view) but the aggregationRule labels do not match.

How to Reproduce the Error

Create a ServiceAccount with no bindings and try to list pods as it:

kubectl create serviceaccount deployer -n staging
kubectl auth can-i list pods -n staging \
  --as=system:serviceaccount:staging:deployer
no
kubectl get pods -n staging \
  --as=system:serviceaccount:staging:deployer
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:staging:deployer" cannot list resource "pods" in API group "" in the namespace "staging"

Diagnostic Commands

Impersonate the subject and ask whether the exact action is allowed:

kubectl auth can-i list pods -n staging \
  --as=system:serviceaccount:staging:deployer

List every permission the subject actually has — invaluable for spotting what is missing:

kubectl auth can-i --list -n staging \
  --as=system:serviceaccount:staging:deployer
Resources                  Non-Resource URLs   Resource Names   Verbs
selfsubjectreviews.authorization.k8s.io   []   []   [create]
*.*                        [/api/*]            []               []

Find every binding that references the subject across the cluster:

kubectl get rolebindings,clusterrolebindings -A -o wide \
  | grep -E 'deployer|NAMESPACE'
NAMESPACE   NAME            ROLE                  AGE   USERS   GROUPS   SERVICEACCOUNTS
staging     deploy-binding  Role/deploy-role      2d            staging/deployer

Inspect what a referenced Role or ClusterRole actually permits:

kubectl describe role deploy-role -n staging
PolicyRule:
  Resources   Non-Resource URLs   Resource Names   Verbs
  ---------   -----------------   --------------   -----
  pods        []                  []               [get watch]   # note: no "list"!

That output reveals the bug: the rule grants get and watch but not list.

Step-by-Step Resolution

1. Confirm the gap with kubectl auth can-i --list so you know exactly which verb/resource/scope is missing.

2. Decide the scope. Namespaced resource (pods, deployments, services) → Role + RoleBinding in the target namespace. Cluster-scoped resource (nodes, namespaces, persistentvolumes) → ClusterRole + ClusterRoleBinding.

3. Create a Role granting the missing verbs (note apiGroups: [""] for core resources):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: staging
rules:
  - apiGroups: [""]          # "" is the core API group (pods, services, configmaps)
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]

4. Bind it to the subject. For a ServiceAccount, the subject kind is ServiceAccount with its own namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: deployer-pod-reader
  namespace: staging          # MUST match the namespace you want access in
subjects:
  - kind: ServiceAccount
    name: deployer
    namespace: staging
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

5. Apply and verify:

kubectl apply -f pod-reader-role.yaml -f deployer-binding.yaml
kubectl auth can-i list pods -n staging \
  --as=system:serviceaccount:staging:deployer
yes

6. For cluster-scoped access, swap both kinds to their cluster equivalents — a ClusterRole with resources: ["nodes"] bound by a ClusterRoleBinding. A namespaced RoleBinding can never authorize kubectl get nodes.

Prevention and Best Practices

  • Grant the least privilege that works: scope to specific namespaces and resources rather than * and cluster-admin.
  • Bind to groups (e.g. an OIDC group) instead of individual users so onboarding does not require new RBAC objects each time.
  • Remember the scope rule: namespaced resources need Role/RoleBinding; cluster-scoped resources need ClusterRole/ClusterRoleBinding. Mixing them is the single most frequent mistake.
  • Double-check the apiGroup: core resources use "", deployments use apps, ingresses use networking.k8s.io. A wrong group silently grants nothing.
  • Reuse the built-in view, edit, and admin ClusterRoles via RoleBinding instead of hand-writing rules where they fit.
  • Audit access in CI with kubectl auth can-i --list --as=<subject> so a missing binding fails the pipeline, not production.
  • error: You must be logged in to the server (Unauthorized) — a 401, meaning authentication failed entirely. Contrast 401 (who are you?) vs 403 (you may not do this) in the Unauthorized error guide.
  • Error from server (Forbidden): ... cannot create resource ... at the cluster scope — same RBAC failure for a cluster-scoped verb; needs a ClusterRole.
  • Error from server (Forbidden): ... requires one of ["...","..."] but ServiceAccount ... has none — a PodSecurity admission denial, not RBAC; check Pod Security Standards labels.
  • the server doesn't have a resource type "..." — the resource/CRD does not exist; distinct from being forbidden to access it.

Frequently Asked Questions

What is the difference between this 403 and a 401 Unauthorized? A 401 Unauthorized means the API server could not authenticate you — bad or missing credentials. A 403 Forbidden (“cannot list resource”) means you are authenticated but no RBAC rule grants the action. Fix 401 by sorting out your token or kubeconfig; fix 403 by adding a Role/RoleBinding. See the Unauthorized guide for the 401 path.

My RoleBinding exists but I’m still forbidden — why? Check three things: the RoleBinding is in the same namespace you are querying, the subject kind/name/namespace exactly matches the identity in the error, and the referenced Role actually lists the verb you need. A binding in default does nothing for requests against staging.

Why does kubectl get nodes fail even though my user can list pods? nodes is a cluster-scoped resource. A namespaced Role/RoleBinding can only grant namespaced resources. You need a ClusterRole granting list on nodes bound with a ClusterRoleBinding.

How do I see exactly what a ServiceAccount is allowed to do? Impersonate it: kubectl auth can-i --list --as=system:serviceaccount:<ns>:<name>. This enumerates every resource/verb the subject can access, making missing permissions obvious without trial-and-error.

What does API group "" mean in the error? The empty string is the core API group — it covers pods, services, configmaps, secrets, and nodes. In a Role rule you write apiGroups: [""] to grant access to those resources; using any other group there will not match the request.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.