DEV Community

Xjz · Builder
Xjz · Builder

Posted on

Stuck on Casbin's model.conf? 5 mistakes beginners hit most

Stuck on Casbin's model.conf? The 5 mistakes beginners hit most (with a runnable fix)

Casbin is one of the few permission frameworks that works across languages (Go / Java / Python / Node…), and its architecture is refreshingly clean: model.conf defines the rules, the policy stores the data. But nearly every beginner stumbles on "those few lines of rules" — especially when everything just returns false and you have no idea which line is wrong.

The 5 mistakes below are the ones that show up over and over in real community questions, ordered by how often beginners hit them.

1. matcher field names don't line up with request/policy

Whatever columns [request_definition] and [policy_definition] declare are all the matcher can use, and order matters:

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
Enter fullscreen mode Exit fullscreen mode

If your request is alice, read, data1 but you meant "alice reads data1", you may have swapped the action and the object. Wrong order = all false, and no error is raised — the hardest thing to debug.

2. Not understanding what g() actually does

g(r.sub, p.sub) is not magic. It means: is r.sub equal to p.sub, directly or through role inheritance? The role graph is defined both by [role_definition] (g = _, _) and by g rows in the policy.

A common trap: thinking g = _, _ alone is enough. It isn't — roles and who belongs to whom must also be written as g, user, role rows in the policy, otherwise no user ever matches.

3. p.eft + [policy_effect] acting weird together

[policy_effect] decides how multiple matched policies collapse into one result:

# allow wins: one allow is enough
e = some(where (p.eft == allow))

# explicit deny overrides allow (IAM style)
e = priority(p.eft) || deny
Enter fullscreen mode Exit fullscreen mode

Each policy is allow by default. If you need "this user/team is banned", don't copy every rule — use an eft column plus priority(p.eft) || deny and deny rules just win. Much cleaner policy tables.

4. Missing role-graph rows = everything that routes through g fails

The policy needs both halves:

p, admin, /data, read
g, alice, admin
Enter fullscreen mode Exit fullscreen mode

Only then will g(r.sub, p.sub) let alice match /data read. Drop the g, alice, admin row and alice belongs to no role — nearly everything denies.

5. "Cache not updating" at the integration layer

If you use syncedCachedEnforcer (the cached synced enforcer), updating the policy but seeing the same enforce result usually means the cache wasn't refreshed — not that the rules are wrong. Check cache first, then go back to model.conf.


Honest ask: I want to build a tool to fix exactly this — tell me what's missing

Digging through the reports, the root issue is the same everywhere: model.conf is written for machines, not humans — and a wrong line fails silently with a quiet false.

So we're collecting real cases to build a small tool: you describe rules in plain words ("alice can read docs, bob owns project X, editors can write, nobody from team-ban is allowed") and it generates a runnable model.conf + sample policy, with a plain-English explanation of why each request returns true/false.

If you'd also like to stop debugging model.conf at 2am, please comment below with the single step that tripped you up the most — or join the conversation on GitHub (it's the onboarding research thread):

github.com/apache/casbin/discussions — search "onboarding" (titled "What blocked you most when getting started with Casbin?")

One real pain point from you could be the first 10% this tool should focus on.

Top comments (0)