{"record":{"id":"7352fb31364ddede","repo":"argoproj/argo-workflows","slug":"was-unable-to-determine-level-for-s","errorCode":null,"errorMessage":"was unable to determine level for %s","messagePattern":"was unable to determine level for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/sync/sync_manager.go","lineNumber":266,"sourceCode":"\t\tif template.Synchronization != nil {\n\t\t\tsyncItems, err := allSyncItems(template.Synchronization)\n\t\t\tif err != nil {\n\t\t\t\treturn ErrorLevel, err\n\t\t\t}\n\t\t\tfor _, syncItem := range syncItems {\n\t\t\t\tsyncLockName, err := syncItem.lockName(wf.Namespace)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlastErr = err\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif lockName == syncLockName.String(ctx) {\n\t\t\t\t\treturn TemplateLevel, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif lastErr == nil {\n\t\tlastErr = fmt.Errorf(\"was unable to determine level for %s\", lockName)\n\t}\n\treturn ErrorLevel, lastErr\n}\n\n// initFailureFatal reports whether a failure to (re)establish a lock at startup\n// is unrecoverable and must fail closed (crashloop). Only two cases qualify:\n//   - the lock name is undecodable, so there is no key to poison under and no way\n//     to prove the workflow's spec re-acquires the same lock; and\n//   - the lock is database-backed but no database session is configured, so\n//     nothing can back the lock.\n//\n// Everything else - a transient ConfigMap/DB read failure, a limit fetch\n// returning 0 - is recoverable: the name decodes, so the lock can be poisoned\n// (the poison key matches what a racer would compute) without halting the whole\n// controller.\nfunc (sm *Manager) initFailureFatal(ctx context.Context, lockName string) bool {\n\tdecoded, err := DecodeLockName(ctx, lockName)\n\tif err != nil {","sourceCodeStart":248,"sourceCodeEnd":284,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/sync/sync_manager.go#L248-L284","documentation":"getWorkflowSyncLevelByName walks the workflow's synchronization references (workflow-level, template-level, template default) to determine at which level a lock with the given name was declared. If no level matches and no earlier error was recorded, it returns ErrorLevel with 'was unable to determine level for <lockName>'.","triggerScenarios":"upgradeHolderKey asks for the sync level of a holder's lock during controller reconciliation; the lockName string doesn't match any semaphore/mutex declared anywhere in the workflow (renamed lock, deleted node, stale holder entry).","commonSituations":"Workflow spec edited to rename a semaphore/mutex while old holds persisted; node status referencing a lock from a removed template; decoding a lock name that never belonged to the workflow.","solutions":["Verify the lock name in the error matches a semaphore/mutex declared in the workflow spec; fix renames or revert the spec change","Retry/re-submit the workflow to rebuild holder state consistent with the current spec","Clean stale node/holder status entries that reference the old lock name"],"exampleFix":"// before: renamed lock with stale holders\nsynchronization:\n  semaphore:\n    configMapKeyRef: {name: cm, key: new-key}\n// after: keep the key stable across edits, or re-submit\nsynchronization:\n  semaphore:\n    configMapKeyRef: {name: cm, key: original-key}","handlingStrategy":"try-catch","validationCode":"// verify the lock name appears in the workflow spec before relying on holds\nfunc declaresLock(wf *Workflow, lockName string) bool {\n    return strings.Contains(fmt.Sprintf(\"%v\", wf.Spec.Synchronization), filepath.Base(lockName))\n}","typeGuard":null,"tryCatchPattern":"level, err := sm.GetWorkflowSyncLevelByName(ctx, wf, lockName)\nif err != nil && strings.Contains(err.Error(), \"unable to determine level\") {\n    return fmt.Errorf(\"lock %q not declared in workflow spec; fix spec or rebuild state: %w\", lockName, err)\n}","preventionTips":["Keep semaphore/mutex names stable across workflow edits, or re-submit instead of editing in place","Use `argo lint` after renaming locks to catch stale references","Purge stale node status when removing synchronization from templates"],"tags":["sync-level","lock-name","reconciliation"],"backgroundTag":"lock-level-unresolved","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}