ash-project / ash-project/ash

Defining `get_by` on a code interface doesn't do type-checking of the argument

Open
#1,971 6 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Elixir
Stars
2.5k
Forks
422
Avg merge
23h 26m
Merged PRs (30d)
46

Description

**Describe the bug**

A code interface to get a single record by UUID primary key:

```elixir
define :get_artist_by_id, action: :read, get_by: :id
```

Calling the generated function with an invalid UUID raises an error from within Ecto:

```
iex(8)> Tunez.Music.get_artist_by_id("not a uuid")
{:error,
%Ash.Error.Invalid{
bread_crumbs: ["Error returned from: Tunez.Music.Artist.read"],
query: "#Query<>",
errors: [
%Ash.Error.Query.InvalidFilterValue{
message: nil,
value: "not a uuid",
context: #Ecto.Query,
splode: Ash.Error,
bread_crumbs: ["Error returned from: Tunez.Music.Artist.read"],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
```

Whereas if the code interface used a real action that applied a `get_by` filter:

```elixir
# in the domain
define :get_artist_by_id, action: :get_by_id, args: [:id]

# in the resource
actions do
read :get_by_id do
get_by :id
end
end
```

Calling the function would properly validate the UUID before running the action:

```
iex(5)> Tunez.Music.get_artist_by_id("not a uuid")
{:error,
%Ash.Error.Invalid{
errors: [
%Ash.Error.Query.InvalidArgument{
field: :id,
message: "is invalid",
value: "not a uuid",
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
```

I expected these two function definitions to be exactly equivalent, but that is not the case. The `get_by` code interface version skips past the UUID type check, and the non-UUID value gets all the way into the Ecto query.

**Runtime**
- Elixir version 1.18.2-otp-27
- Erlang version 27.2.1
- OS macOS Sequoia
- Ash version 3.5.4

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the two code-interface definitions shown in the issue and compare their argument handling for an invalid UUID. Trace the `get_by` code-interface path and the real action path to find where validation differs. Done means both forms reject the invalid value as an `InvalidArgument` before it reaches the Ecto query.

Written by the indexing model from the issue text.

Assessment

Tech stack
elixir
Domain
backend-api-design
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.