semaphoreui/semaphore · error

error at workflows[ ]

Error message

error at workflows[%d]: %s

What it means

This error wraps a per-item failure from BackupFormat.Verify() while validating backup.Workflows: the workflow at index i failed o.Verify(backup). A workflow entry in the backup is invalid — commonly nodes/edges referencing templates, inventories, or other nodes not present in the backup — so the whole backup fails validation before restore begins.

Solutions

  1. Read the inner error after 'workflows[i]:' to identify the failing node or field
  2. Fix the workflow entry at that index, ensuring all referenced templates/inventories exist in the backup
  3. Validate the backup JSON structure (nodes/edges/name) before calling Verify again
  4. Re-export a fresh backup from the source instance rather than editing the workflow graph by hand
Defensive patterns

Strategy: validation

Validate before calling

for i, w := range backup.Workflows {
    if w == nil || w.Name == "" || len(w.Nodes) == 0 {
        return fmt.Errorf("workflows[%d]: invalid workflow before restore", i)
    }
}
if err := backup.Verify(); err != nil {
    return err
}

Type guard

func validWorkflow(w *WorkflowBackup) bool { return w != nil && w.Name != "" && len(w.Nodes) > 0 }

Try / catch

if err := backup.Verify(); err != nil {
    if strings.Contains(err.Error(), "workflows[") {
        // locate the failing workflow and its node reference
    }
    return err
}

Prevention

When it happens

Trigger: Calling BackupFormat.Verify() where the workflow at backup.Workflows[i] fails Verify — e.g. missing workflow name, node referencing a template absent from backup.Templates, or malformed edges.

Common situations: Backups whose workflow graphs reference entities that were hand-removed; version drift between exporting and importing Semaphore instances; truncated or concatenated backup files.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at services/project/restore.go:588

	}
	for i, o := range backup.Templates {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at templates[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Roles {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at roles[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Runners {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at runners[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Workflows {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at workflows[%d]: %s", i, err.Error())
		}
	}

	return nil
}

func (backup *BackupFormat) Restore(user db.User, store db.Store, workflowStore db.WorkflowManager) (*db.Project, error) {
	var b = BackupDB{store: store, workflowStore: workflowStore}
	project := backup.Meta.Project

	// Prevent importing a project with a name that already exists
	existingProjects, err := b.store.GetAllProjects()
	if err == nil {
		for _, p := range existingProjects {
			if p.Name == project.Name { // exact name match
				return nil, common_errors.NewValidationError(fmt.Sprintf("project with name '%s' already exists", project.Name))
			}
		}

View on GitHub (pinned to 1774ccb71a)