gastownhall/beads · error
httpapi: the configured provider fires this workspace's hook
Error message
httpapi: the configured provider fires this workspace's hooks; this server does not run hooks, so pass the provider beneath the hook layer (uow.UnwrapProvider)
What it means
This startup validation error means the configured uow.Provider would fire the workspace's hooks on each served mutation. The HTTP server contractually does not run hooks, so a hook-firing provider is refused exactly like a hook-firing role would be — otherwise a user's hook subprocess would run per served mutation. The fix is to wrap the provider beneath the hook layer via uow.UnwrapProvider.
Source
Thrown at internal/httpapi/server.go:672
return errors.New("httpapi: no database source: set Provider, or " + roleSourceNames + " together")
// The conditional role, checked where every other configuration mistake is.
// A workspace that HAS a journal and a server that cannot read it is the
// one combination that would bind, answer every other route, and fail this
// one — with a nil dereference, which is the shape checkDatabaseSource
// exists to prevent. A workspace with the journal off needs no reader and
// this says nothing about it.
case cfg.Provider == nil && cfg.EventsJournalEnabled && cfg.EventsJournal == nil:
return errors.New("httpapi: this workspace's events journal is enabled but no EventsJournal reader was configured; " +
"take one off the store (storage.EventsJournalCursor), or serve a workspace with the journal off")
case anyRoleFiresHooks(cfg):
return errors.New("httpapi: a configured role fires this workspace's hooks; " +
"this server does not run hooks, so take the roles from the store beneath the hook decorator " +
"((*storage.HookFiringStore).Unwrap)")
case uow.ProviderFiresHooks(cfg.Provider):
// The same refusal for the other database source. A provider's roles
// carry whatever the provider carries, so a hook-firing one would run a
// user's subprocess per served mutation just as a hook-firing role does.
return errors.New("httpapi: the configured provider fires this workspace's hooks; " +
"this server does not run hooks, so pass the provider beneath the hook layer " +
"(uow.UnwrapProvider)")
}
return nil
}
// Addr is the bound address, which is the only way to discover the port under
// the ephemeral default.
func (s *Server) Addr() string { return s.listener.Addr().String() }
// Serve accepts requests until ctx is canceled, then drains. It returns nil
// on a clean shutdown; a listener failure is returned as-is.
//
// The drain budget covers a committing request that is mid-retry, because
// Shutdown does not cancel in-flight handler contexts: killing such a
// connection early would leave the client unable to tell whether its write
// landed.
func (s *Server) Serve(ctx context.Context) error {View on GitHub (pinned to 71377f2769)
Solutions
- Pass the provider through uow.UnwrapProvider to get the provider beneath the hook layer before putting it in Config
- Check uow.ProviderFiresHooks(provider) == false before constructing the server
- If hooks must fire on served mutations, use a code path that declares and honors that contract instead of this server
Example fix
// before
srv, err := httpapi.New(httpapi.Config{Provider: hookFiringProvider})
// after
srv, err := httpapi.New(httpapi.Config{Provider: uow.UnwrapProvider(hookFiringProvider)}) Defensive patterns
Strategy: validation
Validate before calling
if uow.ProviderFiresHooks(cfg.Provider) {
return errors.New("provider fires hooks; wrap with uow.UnwrapProvider before serving")
} Try / catch
if err := httpapi.New(cfg); err != nil {
if strings.Contains(err.Error(), "provider fires this workspace's hooks") {
cfg.Provider = uow.UnwrapProvider(cfg.Provider)
return httpapi.New(cfg)
}
return err
} Prevention
- Run uow.ProviderFiresHooks(provider) as an assert in tests for server wiring
- Never reuse the CLI's hook-firing provider directly for the HTTP server
- Keep provider construction and server construction in the same place to preserve the unwrap step
When it happens
Trigger: Constructing the server with a Config whose Provider satisfies uow.ProviderFiresHooks — i.e. a provider that carries hook-firing roles. checkDatabaseSource hits this case only when no role fires hooks (the earlier case) but the provider itself does.
Common situations: Passing a provider built over a hook-wrapped store directly into httpapi.Config, typically after enabling on_update hooks; also arises when reusing a provider meant for the CLI, where hooks firing is desired.
Related errors
- httpapi: a configured role fires this workspace's hooks; thi
- httpapi: this server has no unit-of-work provider; it answer
- --allowed-host is empty; pass the Host header value clients
- multiple .doltcfg directories detected
- dolt directory is required
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c4c9362bfa03a8a8.
Report an issue: GitHub.