Does a player not have a certain permission? There could be many causes and unfortunately, zPermissions has grown into a complex multi-layered beast.
Fortunately, there are commands to inspect permissions at various layers.
See the command reference for more detailed information about each command mentioned below.
But starting at the bottom-most layer...
This where the permissions are actually stored: database tables, data.yml, or something else (if you're using custom storage).
First thing you should ask is: where is the permission defined?
If defined on a player, then use:
/permissions player <player> show
to show permissions specific to that player.
If defined on a group, then use:
/permissions group <group> show
to show permissions specific to that group. Next, you should check that the player is actually a member of that group:
/permissions player <player> groups
Note that this command outputs the groups in order from highest-weight to lowest-weight. Group permissions are applied in reverse order (so the lowest-weight group first). Permissions applied later will override any previously-applied permissions. So if the group with the missing permission is not first in that list, then other groups may have overidden the permission — check them with "
/perm group <group> show" as well.
If the permission is missing at this layer, then... you need to add it. Also double-check any region or world qualifiers you've used.
If the missing permission is the child of another permission, then you probably don't set it directly and you won't see it with the "show" command.
If that's the case, use the "dump" command instead:
/permissions player <player> dump -w <world> -f <permission>
What this does is simulates the expansion of child permissions that zPermissions does before handing them off to Bukkit. The "-w <world>" option specifies the world. If omitted, it will use your world, and if you're actually the console, it will just use the first/main world of your server. Use the "-f <permission>" option to narrow down the output to just a certain permission. Note that this filter uses substring matching. If you omit the "-f" option, then all permissions will be shown.
Note that the "dump" command also supports simulating different regions. See the command reference.
If the child permission is missing then you need to set its parent permission (or parent of parent, etc.) somewhere. Go back to the "Storage" layer and verify that this ancestor permission is set on either the player of one of his/her groups.
At this layer, zPermissions has handed off all permissions to Bukkit. If the missing permission is present in previous layers but missing here, then that is almost certainly a zPermissions bug. (And I would really like to know!)
To check if the permission exists in Bukkit, there are a few commands:
/permissions check <permission> <player>
to check if <permission> is in <player>'s effective Bukkit permission set. <permission> must be the exact name of the permission node you're interested in, and it must not have world or region qualifiers. <player> must be online.
/permissions inspect -f <permission> <player>
This is a more generalized version of "check" — it will dump effective permissions instead. Once again, you can use "-f <permission>" to do a substring filter of the output. Or you can omit the "-f" option to dump all effective permissions.
The final layer are other plugins — the usual consumers of permissions. While zPermissions can't inject debugging code into other plugins, it can perform a simple Player.hasPermission() check, which is probably what most simple plugins use. This will help narrow things down, at least:
/permissions player <player> has <permission>
Again, <permission> must be the exact unqualified permission and <player> must be online.
There's typically one other way plugins can check permissions and that's through Vault. When the player is online, Vault simply calls Player.hasPermissions(). However, when the player is offline then the permissions-plugin-specific bridge/connector/whatever is invoked.
Finding how the plugin checks permissions is usually the first thing I do when someone complains of a particular plugin not seeing permissions. So knowing if Player.hasPermissions() works lets me know whether the problem is potentially on the Vault side or zPermissions side of things.