gastownhall/beads · error
httpapi: this server has no unit-of-work provider; it answer
Error message
httpapi: this server has no unit-of-work provider; it answers from configured issue roles
What it means
WithUOW opens a unit of work around fn, but a server configured with issue roles instead of a Provider has no unit of work to open — the roles own their own transactions. Instead of dereferencing a nil provider, WithUOW returns this error stating the server is roles-backed.
Source
Thrown at internal/httpapi/server.go:1228
}
// WithUOW runs fn inside one unit of work and guarantees the rollback.
//
// The close context is DETACHED on purpose. Close sends ROLLBACK on the pinned
// connection, and the transaction layer POISONS that connection if the send
// fails (internal/storage/uow/doltserver_tx.go) — go-sql-driver's session reset
// does not clear an open transaction, so a session that may still be in one
// must never go back to the pool. Correctness is therefore safe either way, but
// closing with the request's own canceled context would fail the ROLLBACK
// immediately and burn one pinned session on every client disconnect. Reads
// never commit.
//
// It is provider-only, and says so rather than dereferencing nil: a
// roles-backed server has no unit of work to open, and the roles it does hold
// own their own transactions.
func (s *Server) WithUOW(ctx context.Context, rec *reqInfo, fn func(uow.UnitOfWork) error) error {
if s.provider == nil {
return errors.New("httpapi: this server has no unit-of-work provider; it answers from configured issue roles")
}
start := time.Now()
uw, err := s.provider.NewUOW(ctx)
if rec != nil {
rec.uowWait = time.Since(start)
}
if err != nil {
return err
}
defer func() {
closeCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), uowCloseTimeout)
defer cancel()
uw.Close(closeCtx)
}()
return fn(uw)
}
// acquire takes a database slot, or gives up. A timed-out wait is ErrBusy, notView on GitHub (pinned to 71377f2769)
Solutions
- Configure the server with a Provider if you need unit-of-work semantics in handlers
- Branch on s.provider == nil (or your own config knowledge) and take the roles-backed code path instead of calling WithUOW
- Refactor the handler to use the role-specific APIs, which manage their own transactions
Example fix
// before
err := srv.WithUOW(ctx, rec, func(uw uow.UnitOfWork) error { return mutate(uw) })
// after
if srv.Provider() == nil {
return mutateWithRoles(srv, ctx)
}
return srv.WithUOW(ctx, rec, func(uw uow.UnitOfWork) error { return mutate(uw) }) Defensive patterns
Strategy: type-guard
Validate before calling
if srv.Provider() == nil {
return errors.New("roles-backed server: use role APIs, not WithUOW")
} Type guard
func hasProvider(s *httpapi.Server) bool { return s.Provider() != nil } Try / catch
err := srv.WithUOW(ctx, rec, fn)
if err != nil && strings.Contains(err.Error(), "no unit-of-work provider") {
return fallbackToRolesPath(ctx, srv)
} Prevention
- Decide the database source (Provider vs roles) once in config and carry that decision into handler code
- Document handlers that require a Provider and refuse to register them on roles-backed servers
- Test both configuration modes so provider-only paths fail loudly in CI
When it happens
Trigger: Calling s.WithUOV/WithUOW (Server.WithUOW) on a server built with Config without Provider set (roles-backed server). Any request handler or middleware that assumes provider-backed configuration and calls WithUOW will get this error.
Common situations: Mixing configuration modes: deployment switched from provider-backed to roles-backed (or vice versa) but a handler still calls WithUOW; embedding the httpapi server in another binary and calling WithUOW unconditionally.
Related errors
- httpapi: the configured provider fires this workspace's hook
- httpapi: a configured role fires this workspace's hooks; thi
- --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/bbe111a4205f3482.
Report an issue: GitHub.