semaphoreui/semaphore · warning
no rows in result set
Error message
no rows in result set
What it means
ErrNotFound is the sentinel error ('no rows in result set') returned by the store layer when a queried/deleted object does not exist. Callers are expected to check with errors.Is and treat it as an expected condition, not a server error.
Solutions
- Check errors.Is(err, db.ErrNotFound) and handle it as a benign case (e.g. return 200/404 to the client) instead of a 500
- Treat 'already deleted' as success in idempotent delete flows, as api/apps.go does
- Verify the identifier exists (or belongs to the current project/environment) before attempting the lookup/delete
Example fix
// before
if err := store.DeleteOptions("apps."+appID); err != nil {
helpers.WriteError(w, err, http.StatusInternalServerError)
}
// after
err := store.DeleteOptions("apps." + appID)
if err != nil && !errors.Is(err, db.ErrNotFound) {
helpers.WriteError(w, err, http.StatusInternalServerError)
} Defensive patterns
Strategy: try-catch
Validate before calling
exists, err := store.GetApp(tx, appID)
if err != nil && !errors.Is(err, db.ErrNotFound) {
return err
}
if exists == nil { /* skip delete or return 404 */ } Try / catch
err := store.DeleteOptions("apps." + appID)
if err != nil && !errors.Is(err, db.ErrNotFound) {
helpers.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
// ErrNotFound: treat as already deleted (idempotent success) Prevention
- Always compare with errors.Is(err, db.ErrNotFound), never string equality
- Treat deletes as idempotent operations
- Check entity existence in the UI before offering delete actions
When it happens
Trigger: Calling deleteApp (api/apps.go:80) for an app ID that no longer exists or was already deleted; login/external-user lookups with unknown identifiers; any store Get/Delete targeting a missing row.
Common situations: Double-delete races (two requests deleting the same app); stale UI deleting an entity removed by someone else; login attempts with wrong usernames; IDs from another environment.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- invalid operation
- Failed to link external account.
- Internal Server Error
- no admins found in database; create a admin first
- user with login not found
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/39e92beba29354a8.
Report an issue: GitHub.
Appendix: source
Thrown at db/Store.go:158
}
// ObjectProps describe database entities.
// It mainly used for NoSQL implementations (currently BoltDB) to preserve same
// data structure of different implementations and easy change it if required.
type ObjectProps struct {
TableName string
Type reflect.Type // to which type the table bust be mapped.
IsGlobal bool // doesn't belong to other table, for example to project or user.
ReferringColumnSuffix string
PrimaryColumnName string
SortableColumns []string
DefaultSortingColumn string
SortInverted bool // sort from high to low object ID by default. It is useful for some NoSQL implementations.
Ownerships []*ObjectProps
SelectColumns []string
}
var ErrNotFound = errors.New("no rows in result set")
var ErrInvalidOperation = errors.New("invalid operation")
type TaskStatUnit string
const TaskStatUnitDay TaskStatUnit = "day"
const TaskStatUnitWeek TaskStatUnit = "week"
const TaskStatUnitMonth TaskStatUnit = "month"
type TaskFilter struct {
Start *time.Time `json:"start"`
End *time.Time `json:"end"`
UserID *int `json:"user_id"`
Status []task_logger.TaskStatus
}
type TaskStat struct {
Date string `json:"date"`
CountByStatus map[task_logger.TaskStatus]int `json:"count_by_status"`View on GitHub (pinned to 1774ccb71a)