bytebase/bytebase · error
project %s not found or not marked as deleted
Error message
project %s not found or not marked as deleted
What it means
DeleteProjects physically purges projects that were previously soft-deleted (marked as deleted). It collects the resourceIDs actually purged in the transaction, then verifies every requested resourceID was purged. If a requested project is missing or not marked as deleted, it aborts with "project %s not found or not marked as deleted" and rolls back. This guards against purging live or unknown projects.
Source
Thrown at backend/store/project.go:727
rows, err := tx.QueryContext(ctx, sql, args...)
if err != nil {
return errors.Wrapf(err, "failed to delete projects %s", projectList)
}
defer rows.Close()
var purged []string
for rows.Next() {
var resourceID string
if err := rows.Scan(&resourceID); err != nil {
return errors.Wrap(err, "failed to scan purged project")
}
purged = append(purged, resourceID)
}
if err := rows.Err(); err != nil {
return errors.Wrapf(err, "failed to read purged projects %s", projectList)
}
for _, resourceID := range resourceIDs {
if !slices.Contains(purged, resourceID) {
return errors.Errorf("project %s not found or not marked as deleted", resourceID)
}
}
if err := tx.Commit(); err != nil {
return errors.Wrap(err, "failed to commit transaction")
}
// Publish invalidation only after the purge commits: every captured
// descendant entry is removed while unrelated cache entries survive.
s.removeProjectDescendantCaches(cacheKeys, resourceIDs)
return nil
}
func GetListProjectFilter(workspace, filter string) (*qb.Query, error) {
if filter == "" {
return nil, nil
}View on GitHub (pinned to 1870550677)
Solutions
- Confirm the project exists and is marked as deleted (SELECT resource_id ... WHERE deleted) before purging
- Re-check for typos or stale resourceIDs in the caller's delete request
- If the purge already ran once, treat this as success (idempotent retry) rather than an error
- Serialize soft-delete and purge in one transaction to avoid the race
Example fix
// before
for _, resourceID := range resourceIDs {
if !slices.Contains(purged, resourceID) {
return errors.Errorf("project %s not found or not marked as deleted", resourceID)
}
}
// after
for _, resourceID := range resourceIDs {
if !slices.Contains(purged, resourceID) {
if exists, deleted, err := projectState(ctx, tx, resourceID); err == nil && !exists {
continue // already purged; idempotent
} else if err == nil && !deleted {
return errors.Errorf("project %s not marked as deleted", resourceID)
}
return errors.Errorf("project %s not found or not marked as deleted", resourceID)
}
} Defensive patterns
Strategy: validation
Validate before calling
// before purging, verify the project is soft-deleted
var deleted bool
err := db.QueryRowContext(ctx,
`SELECT EXISTS(SELECT 1 FROM project WHERE resource_id = $1 AND deleted = TRUE)`,
resourceID).Scan(&deleted)
if err != nil || !deleted {
return errors.Errorf("project %s not marked as deleted; skip purge", resourceID)
} Try / catch
if err := store.DeleteProjects(ctx, resourceIDs); err != nil {
if strings.Contains(err.Error(), "not found or not marked as deleted") {
// already purged or wrong id: treat as idempotent success after verification
return nil
}
return err
} Prevention
- Verify soft-deleted state before issuing purges
- Make purge retries idempotent (skip already-purged ids)
- Serialize soft-delete and purge to avoid racing restores
When it happens
Trigger: Calling DeleteProject or BatchDeleteProjects with a resourceID that either does not exist or whose row is not flagged as soft-deleted at purge time — e.g. purging before the soft-delete committed, or after the row was already physically removed.
Common situations: Race between soft-delete and purge; retrying a purge that already succeeded (second pass finds nothing); callers passing wrong or typo'd resourceIDs; concurrent workflows restoring the project between delete and purge.
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
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/09ea46ea20f2c097.
Report an issue: GitHub.