Troubleshooting
Stored IDs look negative
Cause: Discord snowflakes are 64-bit ulong values. Many relational providers
lack native unsigned 64-bit support and store integers as signed long. Snowflakes
with the high bit set appear negative when cast to long.
Fix: This is expected. Persistord uses a bit-faithful unchecked((long)value)
cast so the 64 bits are reinterpreted without range checking — the value round-trips
exactly. No data is lost. See Snowflake Conversion.
Soft-deleted messages are missing from queries
Cause: ApplyMessagesModule() installs a global EF Core query filter that hides
rows where IsDeleted = true. This filter is on by default.
Fix: Use IgnoreQueryFilters() for a single query, or disable the filter globally
at startup with ApplyMessagesModule(filterDeleted: false). See
Soft-delete & Query Filters.
// Per-query
var all = db.Messages.IgnoreQueryFilters().ToList();
// Global — disable at startup
modelBuilder.ApplyMessagesModule(filterDeleted: false);
dotnet ef can't find the DbContext
Cause: dotnet ef looks for a DbContext in the startup project. Persistord
ships no migrations and no executable startup project.
Fix: Pass --project pointing at your bot project, which owns the derived
context:
dotnet ef migrations add Initial --project YourBot.csproj
dotnet ef database update --project YourBot.csproj
See Migrations.
Owned embed collections create extra tables
Cause: EF Core synthesizes surrogate shadow keys for owned collection types stored
relationally. Embed, EmbedField, AttachmentEntity, and ReactionEntity each
get their own table with a generated key. This is expected behaviour.
Fix: If you prefer document storage over relational child tables, use e.ToJson()
on a provider with strong JSON support (PostgreSQL, SQL Server):
modelBuilder.Entity<MessageEntity>().OwnsMany(m => m.Embeds, e =>
{
e.ToJson(); // power-user opt-in — not the default
});
ToJson() is not the default because owned-collection JSON support varies by
provider. See Messages.
History foreign-key violation when deleting a message
Cause: MessageHistoryEntity holds a real foreign key to MessageEntity
configured with DeleteBehavior.Restrict. Hard-deleting the parent MessageEntity
row violates this constraint and is blocked by the database.
Fix: Never hard-delete messages. Set IsDeleted = true and DeletedAt instead
(soft-delete). The row survives physically, the FK stays valid, and the history —
including the row logging the deletion — remains intact. See
Soft-delete & Query Filters.