You have specified a default value for a type that cannot be explicitly converted to an Ecto default

I’m having some trouble with EctoMigrationDefault – probably because I’m a novice with Elixir custom types and protocols, and I’m clearly getting something a bit wrong here.

10:09:57.706 [warning] You have specified a default value for a type that cannot be explicitly
converted to an Ecto default:

  `[:value_add, :non_value_add]`

The default value in the migration will be set to `nil` and you can edit
your migration accordingly.

To prevent this warning, implement the `EctoMigrationDefault` protocol
for the appropriate Elixir type in your Ash project, or configure its
default value in `migration_defaults` in the postgres section. Use `\"nil\"`
for no default.

This is coming from:

# activity_stereotype.ex
  attributes do
    attribute :allowed_types, {:array, COE.Types.ActivityTypes} do
      allow_nil? false
      default COE.Types.ActivityTypes.values
    end
    ...
  end

Trying to resolve this using EctoMigrationDefault I ended up here:

defmodule COE.Types.ActivityTypes do
  @moduledoc ~S"""
  Provides the allowed types for an `COE.Walk.ActivityStereotype`. Types are typically defined as either value add or non-value add.
  """

  use Ash.Type.Enum, values: [:value_add, :non_value_add] # ~w(value_add non_value_add)a

  @type t :: [:atom] 

  def to_string, do: inspect(Enum.map(values(), &(to_string(&1))))
end

defimpl EctoMigrationDefault, for: COE.Types.ActivityTypes do
  def to_default(t), do: to_string(t)
end

Like I said, a novice with custom types (and protocols). With the above in place, I still get the warning about “a type that cannot be explicitly converted to an Ecto default.”

Footnote: I prefer the EctoMigrationDefault approach, but I did note that I can shut up the warning by adding migration_defaults [allowed_types: COE.Types.ActivityTypes.to_string] to the postgres block. But I don’t like how that creates an extra dependency on having a postgres block.

Honestly, the EctoMigrationDefault isn’t the right answer here. You’d have to implement the protocol for arrays generically. We’re kind of moving away from EctoMigrationDefault in general anyway. I would do this:

postgres do
   migration_defaults [allowed_types: "[:value_add, :non_value_add]"]
end

EDIT: sorry, just saw your footnote. I’d advise against using EctoMigrationDefault in general unless the value you’re using it for is a struct (like Money) and the type is not in your control (because if it is there is a callback you can use to set the default value).

1 Like

Somewhere between ~2.x and 3.4, this stopped working. I’m trying to figure out the new and improved syntax for migration_defaults with an Ash.Enum. After upgrading, I’m getting:

** (ArgumentError) unknown default `:non_value_add` for type `:text`. :default may be a string, number, boolean, list of strings, list of integers, map (when type is Map), or a fragment(...)

But of course… taking it out results in:

17:10:40.858 [warning] You have specified a default value for a type that cannot be explicitly
converted to an Ecto default:

  `[:value_add, :non_value_add]`

The default value in the migration will be set to `nil` and you can edit
your migration accordingly.

To prevent this warning, implement the `EctoMigrationDefault` protocol
for the appropriate Elixir type in your Ash project, or configure its
default value in `migration_defaults` in the postgres section. Use `\"nil\"`
for no default.

And I recall we had a previous conversation (above) about how EctoMigrationDefault isn’t really the right solution here.

After futzing with it for some time (all afternoon!) I’ve given up and taken the default out. Just set it in code. But… sure would be nice to have it database enforced…

What did you put in migration_defaults?

It was originally what you had suggested above. As that seems to have led to problems I started digging into syntax changes, but haven’t found anything that worked. In the meantime, I’ve also realized there are some other, deeper problems (several tests failing), so I think the migration to 3.4 has not gone as well as I initially thought.

Finally got around to solving this so I could shut up the build warning.

Ended up changing it to this:

    migration_defaults [allowed_types: "\"{non_value_add,value_add}\""]

The source attribute is:

    attribute :default_type, :atom do
      constraints [one_of: COE.Types.ActivityTypes.values]
      default :value_add
      allow_nil? false
    end

And the referenced enum is:

defmodule COE.Types.ActivityTypes do
  use Ash.Type.Enum, values: [:value_add, :non_value_add]
end

(The point, of course, is to just consolidate the types in case additional types are added in the future, don’t want to have lots of [:valid_add, :non_value_add] scattered throughout my code that I’d have to hunt down and fixup).

Took a while to figure this out – until it occurred to me, just look in the database, see what is getting stuffed into the default_type column, and turn it into a literal string in migration_defaults.

Would be nice if migration_defaults [allowed_types: COE.Walk.ActivityTypes] would work, but it doesn’t put a properly stringified value out… so it all blows up trying to run initial migrations with:

unknown default `COE.Types.ActivityTypes` for type `{:array, :text}`. :default may be a string, number, boolean, list of strings, list of integers, map (when type is Map), or a fragment(...)

I could probably add a to_string function… but posting this in case I’m missing something important here… plus, the required string isn’t really what I’d expect an enum to spit out “naturally” when converting to a string. Meh. Maybe call the function to_migration_string:stuck_out_tongue:

FYI @zachdaniel since this is a definite change from the old (~2.4 or whatever it was written on). Quite probably intentional, but it seems a bit less functional than the older implementation.