semaphoreui/semaphore · error

error at keys[ ]

Error message

error at keys[%d]: %s

What it means

This error wraps a per-item failure from BackupFormat.Verify() while validating backup.Keys: each key object's Verify(backup) is called, and any returned error is re-wrapped as "error at keys[%d]: %s". It means the key entry at the given index in the backup file is invalid (missing fields, broken references to the project backup, etc.) and the backup cannot be restored until it is fixed.

Solutions

  1. Read the inner error after 'keys[i]:' — it names the exact key validation failure
  2. Open the backup file and fix or remove the key entry at the reported index
  3. Re-export the backup from the same Semaphore version instead of hand-editing
  4. Run backup.Meta/Keys through a JSON schema check before calling Restore

Example fix

// before
"keys": [{"name": ""}]
// after
"keys": [{"name": "deploy-key", "type": "ssh", "project_id": 0}]
Defensive patterns

Strategy: validation

Validate before calling

for i, k := range backup.Keys {
    if k == nil || k.Name == "" {
        return fmt.Errorf("keys[%d]: missing name before restore", i)
    }
}
if err := backup.Verify(); err != nil {
    return err
}

Type guard

func validKey(k *KeyBackup) bool { return k != nil && k.Name != "" }

Try / catch

if err := backup.Verify(); err != nil {
    var idxErr *IndexError
    if errors.As(err, &idxErr) { /* inspect entry at idxErr.Index */ }
    return fmt.Errorf("backup validation failed: %w", err)
}

Prevention

When it happens

Trigger: Calling BackupFormat.Verify() (directly or via project restore) where the key at backup.Keys[i] fails its own Verify(backup) — e.g. missing name/type, duplicate key name, or references to entities not present in the backup.

Common situations: Hand-edited or partially exported backup.json; backups generated by a different Semaphore version whose key schema changed; truncated or merged backups missing referenced entities.

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/ffcf9469e7928c5f. Report an issue: GitHub.

Appendix: source

Thrown at services/project/restore.go:553

func (backup *BackupFormat) Verify() error {
	for i, o := range backup.Environments {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at environments[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Views {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at views[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Schedules {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at templates[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Keys {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at keys[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Repositories {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at repositories[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Inventories {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at inventories[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.SecretStorages {
		if err := o.Verify(backup); err != nil {
			return fmt.Errorf("error at secret storage[%d]: %s", i, err.Error())
		}
	}
	for i, o := range backup.Templates {

View on GitHub (pinned to 1774ccb71a)