varvet/pundit · error · KeyError
No permissions in example metadata, did you forget to wrap w
Error message
No permissions in example metadata, did you forget to wrap with `permissions :show?, ...`?
What it means
Pundit's RSpec `permit` matcher reads the permission list from the current example's metadata, which is only populated by the `permissions :show?, ... do ... end` block from Pundit::RSpec::DSL (rspec.rb:131 stashes `metadata[:permissions]`). Calling the matcher from a plain `it`/`describe` block leaves the key absent, so `metadata.fetch(:permissions)` raises KeyError with this hint.
Source
Thrown at lib/pundit/rspec.rb:104
# :nocov:
end
if ::RSpec.respond_to?(:current_example)
def current_example
::RSpec.current_example
end
else
# :nocov:
# Compatibility with RSpec < 3.0, released 2014-06-01.
def current_example
example
end
# :nocov:
end
def permissions
current_example.metadata.fetch(:permissions) do
raise KeyError, <<~ERROR.strip
No permissions in example metadata, did you forget to wrap with `permissions :show?, ...`?
ERROR
end
end
end
# rubocop:enable Metrics/BlockLength
end
# Mixed in to all policy example groups to provide a DSL.
module DSL
# @example
# describe PostPolicy do
# permissions :show?, :update? do
# it { is_expected.to permit(user, own_post) }
# end
# end
#
# @example focused example groupView on GitHub (pinned to 06318683c9)
Solutions
- Wrap the example in a permissions block: `permissions :show? do it { is_expected.to permit(user, post) } end`.
- List every predicate the matcher should exercise: `permissions :show?, :update? do ... end` — one failing permission is reported in the matcher's failure message.
- Keep the spec under spec/policies or tagged `type: :policy` so Pundit::RSpec::PolicyExampleGroup (and thus the DSL) is included in the first place.
- To focus a run, append the symbol: `permissions :show?, :focus do ... end` — the DSL strips it and sets focus metadata.
Example fix
# before
describe PostPolicy do
it { is_expected.to permit(user, post) } # KeyError: no :permissions metadata
end
# after
describe PostPolicy do
permissions :show?, :update? do
it { is_expected.to permit(user, post) }
end
end Defensive patterns
Strategy: validation
Validate before calling
# Inside a policy spec, guard before invoking the matcher: unless defined?(RSpec.current_example) && RSpec.current_example.metadata.key?(:permissions) raise ArgumentError, "wrap examples in `permissions :show?, ... do ... end` before using permit" end
Prevention
- Standardize policy specs on the DSL shape: `permissions :verb? do it { is_expected.to permit(user, record) } end`.
- Add a rubocop-rspec custom rule or shared example that fails when a `permit` matcher appears in an example group without a :permissions metadata ancestor.
- Keep policy specs in spec/policies (or tag them `type: :policy`) so Pundit's DSL and matchers are always loaded where they are used.
When it happens
Trigger: Writing `describe PostPolicy do it { is_expected.to permit(user, post) } end` (no permissions wrapper); moving a `permit` expectation into a shared example or helper method that runs outside a permissions block; using `specify`/custom example groups without the DSL wrapper; keeping specs written against an older style where permissions were inferred rather than declared.
Common situations: Copy-pasting policy specs from blog posts or generators that predate the permissions DSL; refactoring policy specs into shared contexts and losing the wrapper; spec files under spec/policies (or with `type: :policy`) so the DSL is loaded, but examples structured as plain `it` blocks.
Related errors
- You must define #resolve in #{self.class}
- Invalid #<#{policy_scope_class}> constructor is called
- Invalid #<#{klass}> constructor is called
- unable to find scope `#{find(object)}::Scope` for `#{object.
- unable to find policy `#{find(object)}` for `#{object.inspec
AI-assisted analysis of varvet/pundit@06318683c9 (2026-08-21).
Data as JSON: /api/errors/437ccccb9a0350a1.
Report an issue: GitHub.