{"record":{"id":"edf154ba337f55b0","repo":"elsa-workflows/elsa-core","slug":"elsa-platform-runtime-command-poll-was-not-authorized","errorCode":null,"errorMessage":"Elsa Platform runtime command poll was not authorized.","messagePattern":"Elsa Platform runtime command poll was not authorized\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":401,"severity":"error","filePath":"src/modules/Elsa.Platform.Integration/Services/PlatformRuntimeCommandClient.cs","lineNumber":24,"sourceCode":"using Elsa.Platform.Integration.Options;\nusing Microsoft.Extensions.Options;\n\nnamespace Elsa.Platform.Integration.Services;\n\npublic class PlatformRuntimeCommandClient(HttpClient httpClient, IOptions<ElsaPlatformIntegrationOptions> options) : IPlatformRuntimeCommandClient\n{\n    private const string EngineSecretHeaderName = \"X-Elsa-Engine-Secret\";\n    private const string LeaseHeaderName = \"X-Elsa-Command-Lease\";\n    private const string WorkerHeaderName = \"X-Elsa-Worker-Id\";\n    private static readonly JsonSerializerOptions JsonOptions = CreateJsonOptions();\n    private readonly ElsaPlatformIntegrationOptions _options = options.Value;\n\n    public async Task<IReadOnlyList<PlatformRuntimeCommand>> PollAsync(CancellationToken cancellationToken = default)\n    {\n        using var request = CreateRequest(HttpMethod.Get, BuildUri($\"/deployments/runtime/engines/{_options.EngineId:D}/commands\"));\n        using var response = await httpClient.SendAsync(request, cancellationToken);\n        if (response.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)\n            throw new InvalidOperationException(\"Elsa Platform runtime command poll was not authorized.\");\n\n        response.EnsureSuccessStatusCode();\n        var body = await response.Content.ReadFromJsonAsync<PlatformRuntimeCommandListResponse>(JsonOptions, cancellationToken);\n        return body?.Commands ?? [];\n    }\n\n    public async Task<PlatformRuntimeCommandClaimResponse?> ClaimAsync(Guid commandId, CancellationToken cancellationToken = default)\n    {\n        using var response = await SendJsonAsync(\n            BuildUri($\"/deployments/runtime/commands/{commandId:D}/claim\"),\n            new PlatformRuntimeCommandClaimRequest(_options.EngineId, _options.WorkerId, (int)_options.ClaimLeaseDuration.TotalSeconds),\n            cancellationToken);\n\n        if (response.StatusCode == HttpStatusCode.Conflict || response.StatusCode == HttpStatusCode.NotFound)\n            return null;\n\n        response.EnsureSuccessStatusCode();\n        return await response.Content.ReadFromJsonAsync<PlatformRuntimeCommandClaimResponse>(JsonOptions, cancellationToken);","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Platform.Integration/Services/PlatformRuntimeCommandClient.cs#L6-L42","documentation":"PlatformRuntimeCommandClient.PollAsync GETs the engine's command queue from the Elsa Platform. When the Platform responds 401 or 403, the client converts it into this InvalidOperationException instead of parsing the body, signaling the engine secret or worker identity is not authorized.","triggerScenarios":"PollAsync sends a request whose authorization is rejected: missing/invalid lease or auth headers, expired or revoked engine secret, or a WorkerId the Platform does not recognize.","commonSituations":"Rotated the engine secret on the Platform but not in the runtime configuration; wrong environment (staging secret against production endpoint); clock drift invalidating signed credentials; firewall/proxy stripping the auth header.","solutions":["Verify EngineSecret matches a valid secret issued by the Platform for this engine ID; update config and restart.","Confirm EngineId and WorkerId are correct and registered on the Platform for the configured endpoint.","Check the configured PlatformEndpoint points at the intended environment; re-issue credentials if revoked."],"exampleFix":"// before\nbuilder.Services.Configure<ElsaPlatformIntegrationOptions>(o => o.EngineSecret = oldSecret);\n// after\nbuilder.Services.Configure<ElsaPlatformIntegrationOptions>(o => o.EngineSecret = configuration[\"Elsa:PlatformIntegration:EngineSecret\"]!);","handlingStrategy":"try-catch","validationCode":"if (string.IsNullOrWhiteSpace(options.EngineSecret) || string.IsNullOrWhiteSpace(options.WorkerId))\n    throw new InvalidOperationException(\"EngineSecret and WorkerId must be configured before polling.\");","typeGuard":null,"tryCatchPattern":"try { var commands = await client.PollAsync(ct); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"not authorized\"))\n{\n    logger.LogError(ex, \"Platform rejected command poll credentials; check EngineSecret/WorkerId.\");\n    // back off and alert rather than tight-retrying\n}","preventionTips":["Rotate engine secrets on the Platform and in configuration together.","Verify the endpoint environment matches the credentials environment.","Monitor for repeated 401/403 polls and alert on credential drift."],"tags":["http","authorization","platform-integration","api-client"],"backgroundTag":"http-error-response","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}