{"record":{"id":"5910a80cac6185bd","repo":"elsa-workflows/elsa-core","slug":"the-secret-field-name-must-contain-a-letter-or-digit","errorCode":null,"errorMessage":"The secret field name must contain a letter or digit.","messagePattern":"The secret field name must contain a letter or digit\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.ExternalAuthentication.Secrets/Services/ElsaSecretBindingResolver.cs","lineNumber":26,"sourceCode":"/// <summary>\n/// Resolves External Authentication secret references through Elsa Secrets\n/// without exposing secret values or generation metadata to management models.\n/// </summary>\npublic sealed class ElsaSecretBindingResolver(\n    ISecretManager secretManager,\n    IExternalAuthenticationHandleHasher handleHasher) : ISecretBindingResolver, IManagedSecretBindingWriter\n{\n    public const string ResolverType = \"elsa-secrets\";\n    public string Type => ResolverType;\n    string IManagedSecretBindingWriter.ResolverType => ResolverType;\n    string IManagedSecretBindingWriter.DisplayName => \"Elsa Secrets\";\n\n    public async ValueTask<SecretBinding> StageAsync(ManagedSecretBindingWriteRequest request, CancellationToken cancellationToken = default)\n    {\n        ArgumentException.ThrowIfNullOrWhiteSpace(request.ConnectionId);\n        ArgumentException.ThrowIfNullOrWhiteSpace(request.FieldName);\n        if (!request.FieldName.Any(char.IsLetterOrDigit))\n            throw new ArgumentException(\"The secret field name must contain a letter or digit.\", nameof(request));\n\n        // Stage every replacement under a new reference. The caller publishes\n        // that reference with the connection CAS and removes it on CAS failure,\n        // so a stale request can never rotate material used by the live binding.\n        var name = $\"external-authentication:{Guid.NewGuid():N}\";\n        var secret = await secretManager.CreateAsync(new()\n        {\n            Name = name,\n            DisplayName = $\"External authentication {request.FieldName}\",\n            TypeName = SecretTypeNames.Text,\n            StoreName = SecretStoreNames.Encrypted,\n            Value = request.Value.Reveal()\n        }, cancellationToken);\n\n        return new(ResolverType, secret.Name, Ownership: SecretBindingOwnership.Managed);\n    }\n\n    public async ValueTask RemoveAsync(SecretBinding binding, CancellationToken cancellationToken = default)","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.ExternalAuthentication.Secrets/Services/ElsaSecretBindingResolver.cs#L8-L44","documentation":"StageAsync validates that the FieldName of a ManagedSecretBindingWriteRequest contains at least one letter or digit before staging the secret material. This guard prevents field names made entirely of symbols or whitespace from entering the managed secret store, where they would produce unusable or ambiguous secret bindings.","triggerScenarios":"Calling StageAsync with a request whose FieldName is non-empty/whitespace-free but composed entirely of non-alphanumeric characters (e.g. '---', '###', '@$%'). Only characters like '.' or '-' with no letter/digit pass ThrowIfNullOrWhiteSpace but fail this check.","commonSituations":"Configuration values where a field name was templated or trimmed incorrectly, scripting mistakes that emit symbol-only names, or users entering a separator-like value in a Studio secret field form.","solutions":["Ensure request.FieldName contains at least one letter or digit before calling StageAsync","Fix the upstream code or UI that produced a symbol-only field name","Use a descriptive field name such as 'clientSecret' or 'api-key-1'"],"exampleFix":"// before\nawait resolver.StageAsync(new ManagedSecretBindingWriteRequest { ConnectionId = connId, FieldName = \"---\" });\n// after\nawait resolver.StageAsync(new ManagedSecretBindingWriteRequest { ConnectionId = connId, FieldName = \"clientSecret\" });","handlingStrategy":"validation","validationCode":"if (string.IsNullOrWhiteSpace(request.FieldName) || !request.FieldName.Any(char.IsLetterOrDigit))\n    throw new ArgumentException(\"FieldName must contain at least one letter or digit.\", nameof(request.FieldName));","typeGuard":"bool IsValidFieldName(string? name) => !string.IsNullOrWhiteSpace(name) && name.Any(char.IsLetterOrDigit);","tryCatchPattern":null,"preventionTips":["Validate field names at the input/UI layer with a regex like ^(?=.*[A-Za-z0-9]).+$","Never template or build field names from symbol-only strings","Add unit tests covering symbol-only field names"],"tags":["validation","secrets","argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}