Ash.get! returns NotFound with "skipped query run due to filter being false" in async task even with authorize?: false

Hi Zach, I am building a multi-tenant SaaS application using Ash v3 with a schema/context-based multitenancy strategy.

During tenant onboarding, I execute a synchronous form submission that successfully creates a Workspace (a global public resource). Immediately after, I kick off a background setup worker using Phoenix LiveView’s start_async/3 to provision the tenant.

Inside that async worker thread, before any multi-tenancy prefixing occurs, I need to update a global user record in the public schema to lock down their last_active_workspace_id. However, the query fails instantly.

Here is the code inside my async task process:

Accfarm.Shared.User
|> Ash.get!(workspace.owner_id, domain: Accfarm.SharedDomain, authorize?: false)
|> Ash.Changeset.for_update(:update, %{last_active_workspace_id: workspace.id})
|> Ash.update!(domain: Accfarm.SharedDomain, authorize?: false)

And heres my Debug log output

[debug] CREATE SCHEMA IF NOT EXISTS "org_green-valley-hub" [ ]
[debug] Accfarm.Shared.User.read: skipped query run due to filter being false"

🔴 SYSTEM MULTI-TENANT PROVISIONING EXCEPTION: %Ash.Error.Invalid{
errors: [
  %Ash.Error.Query.NotFound{
    primary_key: %{id: "019e87e3-884d-7325-8a74-f938a1d373db"},
    resource: Accfarm.Shared.User
  }
]
}

My Resource Configurations:
Both Workspace and User reside in the public schema and are explicitly configured as global resources:

multitenancy do
  global? true
end

Because authorize?: false is specified, I expected the policy engine to be completely bypassed. Why is Ash generating a filter being false condition on a global public resource read, and how do I execute a system-level override inside a background worker process to update this public data row?

:thinking: So its not only authorization, its any case whereby the filter is false. Hard to say just from the information there why that would be happening though. Are there preparations on the primary read action that could be filtering the action in some way that produce statically false?

"Thanks for the pointer, Zach! That makes total sense. I checked the logs and it is definitely a static filter rewrite preventing the database call entirely.

To help me debug what preparation or base filter is causing this, here is the complete actions do ... end block and attributes layout for my Accfarm.Shared.User resource file:

lib/accfarm/shared/user.ex

multitenancy do
global? true
end

attributes do
uuid_v7_primary_key :id
attribute :email, :ci_string, allow_nil?: false
attribute :last_active_workspace_id, :uuid
attribute :system_role, :atom, default: :user, constraints: [one_of: [:user, :admin]]
timestamps()
end

actions do
defaults [:destroy, :update]

This is the action being invoked by Ash.get!/3

read :read do
primary? true

Are there implicit global preparations or multi-tenancy filters

injected here under the hood by AshPostgres when strategy :context is active elsewhere?

end
end

policies do

policy always(), do: authorize_if(always())
end

Given that this resource has global? true enabled, does the AshPostgres.DataLayer inject implicit tenant routing filters onto primary read actions if other domains in the application use a context/schema multi-tenancy strategy? How can I inspect or trace the exact statically generated expression that is forcing filter being false on this read path?"


Typically this would include a multitenancy attribute, i.e

multitenancy do
  global? true
  attribute :attribute
end

If you’re just saying “it’s not multi tenant” then you’d leave the block out. Its possible that we have an issue where it doesn’t check that there is an attribute and tries to do something silly like nil == nil (i.e no attribute, no tenant)?

"You nailed it perfectly, Zach! That is exactly what was happening.

Both User and Workspace are global public resources that do not have an isolation column attribute because they live exclusively outside the tenant schema boundaries. I had declared a blank multitenancy do global? true end block inside them thinking it was required to declare them as global.

Once that block is evaluated in a background async task without a process tenant set, it triggers that hidden nil == nil state mismatch, which appends a hard where false onto the query builder path [debug].

Removing the multitenancy block entirely from those global public files fixes the query compilation rewrite instantly [debug]! Thanks for the brilliant architectural insight."

Would you mind opening an issue about this on the ash repo?