semaphoreui/semaphore · error

roles[].role does not exist in roles[].name

Error message

roles[].role does not exist in roles[].name

What it means

For non-global template role entries, BackupTemplate.Restore resolves roles[].role as a project-local role name via findEntityByName[db.Role] against BackupDB.roles (project roles restored earlier). This error is thrown when no restored project role matches the name, so the template role permission cannot be created.

Solutions

  1. Add the missing role to roles[] in the backup with an exactly matching name
  2. Fix roles[].role to the name of an existing project role in the backup
  3. Remove the template role entry if the permission is no longer needed
  4. Re-run backup export so role references are regenerated consistently

Example fix

// before
{"roles": [{"name": "developer"}], "templates": [{"roles": [{"role": "dev", "isGlobal": false}]}]}
// after
{"roles": [{"name": "developer"}], "templates": [{"roles": [{"role": "developer", "isGlobal": false}]}]}
Defensive patterns

Strategy: validation

Validate before calling

roleNames := map[string]bool{}
for _, r := range backup.Roles {
    roleNames[r.Name] = true
}
for i, t := range backup.Templates {
    for _, tr := range t.Roles {
        if !tr.IsGlobal && !roleNames[tr.Role] {
            return fmt.Errorf("templates[%d].role %q not in roles[]", i, tr.Role)
        }
    }
}

Try / catch

if err := backup.Verify(); err != nil {
    if strings.Contains(err.Error(), "roles[].role does not exist in roles[].name") {
        return fmt.Errorf("backup references a missing project role: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Restoring a backup whose templates[].roles[].role (isGlobal=false) does not match any roles[].name in the backup, or whose role entry was not restored before the template step.

Common situations: Role deleted or renamed in the backup file; partial restore skipping the roles section; name/case mismatch after manual edits; backup merged from different projects.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/6ec929ecf8f78178. Report an issue: GitHub.

Appendix: source

Thrown at services/project/restore.go:385

				if err != nil {
					return fmt.Errorf("global role does not exist: %s", role.Role)
				}

				_, err = b.store.CreateTemplateRole(db.TemplateRolePerm{
					TemplateID:  newTemplate.ID,
					RoleSlug:    r.Slug,
					ProjectID:   b.meta.ID,
					Permissions: role.Permissions,
				})

				if err != nil {
					return err
				}

				continue
			}
			if k := findEntityByName[db.Role](&role.Role, b.roles); k == nil {
				return fmt.Errorf("roles[].role does not exist in roles[].name")
			} else {
				_, err = b.store.CreateTemplateRole(db.TemplateRolePerm{
					TemplateID:  newTemplate.ID,
					RoleSlug:    k.Slug,
					ProjectID:   b.meta.ID,
					Permissions: role.Permissions,
				})
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func (e BackupIntegration) Restore(b *BackupDB) error {

View on GitHub (pinned to 1774ccb71a)