Framework Error - Assumption failed: Invalid return from calculation

Hi, I’m encountering a Framework Error with the message:
Assumption failed: Invalid return from calculation, expected a value, got %Ash.NotLoaded{}
The full error trace is attached below. I'm unsure whether this is caused by a mistake on my side or if it's a potential bug in the framework. If you need any additional details (resource definitions, calculations, test cases, etc.), I’m happy to provide them. Thanks in advance!
9 Replies
barnabasj
barnabasj3w ago
A reproduction would be great
adonig
adonigOP3w ago
It's a bit messy. I see what I can do to make it minimal 😅
adonig
adonigOP3w ago
This test reproduces the error. It's still a lot of stuff going on. Let me know when you have any questions.
barnabasj
barnabasj3w ago
You were missing load statements in your changes
defmodule BuildingTypeCalculation do
use Ash.Resource.Calculation

@impl true
def load(_query, _opts, _context), do: :_building_type # <= this was missing

@impl true
def calculate(generals, _opts, _context) do
Enum.map(dbg(generals), fn general ->
case Map.get(general, :_building_type) do
nil -> guess_building_type(general)
building_type -> building_type # <= This was passing through the %Ash.NotLoaded{} struct
end
end)
end

defp guess_building_type(_general) do
:single_family
end
end
defmodule BuildingTypeCalculation do
use Ash.Resource.Calculation

@impl true
def load(_query, _opts, _context), do: :_building_type # <= this was missing

@impl true
def calculate(generals, _opts, _context) do
Enum.map(dbg(generals), fn general ->
case Map.get(general, :_building_type) do
nil -> guess_building_type(general)
building_type -> building_type # <= This was passing through the %Ash.NotLoaded{} struct
end
end)
end

defp guess_building_type(_general) do
:single_family
end
end
defmodule SpecificSpaceHeatingDemandCalculation do
use Ash.Resource.Calculation

@impl true
def calculate(heatings, _opts, _context),
do: Enum.map(heatings, fn heating -> guess_specific_space_heating_demand(heating) end)

@impl true
def load(_query, _opts, _context) do
# load only selects the given fields on the related resource
# that's why _building_type and the other fields weren't loaded in
# the nested calculations
[household: [general: [:construction_year]]] # <=
end

def guess_specific_space_heating_demand(heating) do
construction_year = heating.household.general.construction_year

if is_nil(construction_year) do
raise(
ArgumentError,
"Cannot estimate specific space heating demand without construction year"
)
end

case construction_year do
year when year <= 1949 -> 300
year when year <= 1978 -> 200
year when year <= 1995 -> 150
year when year <= 2001 -> 100
year when year <= 2009 -> 75
year when year <= 2015 -> 50
_year -> 40
end
end
end
defmodule SpecificSpaceHeatingDemandCalculation do
use Ash.Resource.Calculation

@impl true
def calculate(heatings, _opts, _context),
do: Enum.map(heatings, fn heating -> guess_specific_space_heating_demand(heating) end)

@impl true
def load(_query, _opts, _context) do
# load only selects the given fields on the related resource
# that's why _building_type and the other fields weren't loaded in
# the nested calculations
[household: [general: [:construction_year]]] # <=
end

def guess_specific_space_heating_demand(heating) do
construction_year = heating.household.general.construction_year

if is_nil(construction_year) do
raise(
ArgumentError,
"Cannot estimate specific space heating demand without construction year"
)
end

case construction_year do
year when year <= 1949 -> 300
year when year <= 1978 -> 200
year when year <= 1995 -> 150
year when year <= 2001 -> 100
year when year <= 2009 -> 75
year when year <= 2015 -> 50
_year -> 40
end
end
end
the load callback is for also for attributes, because they can als be %Ash.NotLoaded{} when they are not selected, so you need to make sure to have them in calc load statements as well. Not just other calculations and relationships and so on
adonig
adonigOP3w ago
so it's not recursively resolving the dependencies of the loaded calculations?
barnabasj
barnabasj3w ago
It does, but the nested calculations didn't have the necessary fields in the loads. _building_type was not specified in any of the calculations
adonig
adonigOP3w ago
Oh now I get it, so the calculations even have to explicitly load the fields on the resource they are attached to, not only the fields from relations. I added all of them and now it works! Thank you very much for the help! 🙇‍♂️
barnabasj
barnabasj3w ago
yeah, exactly. This might have been more explicit in Ash 2.0 https://hexdocs.pm/ash/upgrading-to-3-0.html#calculations-do-not-have-a-select-3-callback-any-more It's mentioned here in the comment for the load statement https://hexdocs.pm/ash/calculations.html#module-calculations but we could probably make it a bit more visible
adonig
adonigOP3w ago
I read through that code comment but because it only mentions: posts: [:title, :body]. I thought it applies only to loading relationship fields. But now that I think about it again, it makes much more sense that you have to explicitly load everything in a calculation, even fields on the same resource.

Did you find this page helpful?