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

  1. Confirm the project exists and is marked as deleted (SELECT resource_id ... WHERE deleted) before purging
  2. Re-check for typos or stale resourceIDs in the caller's delete request
  3. If the purge already ran once, treat this as success (idempotent retry) rather than an error
  4. 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

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.