{"record":{"id":"a814262ca89a3182","repo":"bitwarden/server","slug":"invalid-response-from-slack","errorCode":null,"errorMessage":"Invalid response from Slack.","messagePattern":"Invalid response from Slack\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"src/Api/Dirt/Controllers/SlackIntegrationController.cs","lineNumber":116,"sourceCode":"            throw new NotFoundException();\n        }\n\n        // Fetch token from Slack and store to DB\n        string? callbackUrl = Url.RouteUrl(\n            routeName: \"SlackIntegration_Create\",\n            values: null,\n            protocol: currentContext.HttpContext.Request.Scheme,\n            host: currentContext.HttpContext.Request.Host.ToUriComponent()\n        );\n        if (string.IsNullOrEmpty(callbackUrl))\n        {\n            throw new BadRequestException(\"Unable to build callback Url\");\n        }\n        var token = await slackService.ObtainTokenViaOAuth(code, callbackUrl);\n\n        if (string.IsNullOrEmpty(token))\n        {\n            throw new BadRequestException(\"Invalid response from Slack.\");\n        }\n\n        integration.Configuration = JsonSerializer.Serialize(new SlackIntegration(token));\n        await integrationRepository.UpsertAsync(integration);\n\n        var location = $\"/organizations/{integration.OrganizationId}/integrations/{integration.Id}\";\n        return Created(location, new OrganizationIntegrationResponseModel(integration));\n    }\n}\n","sourceCodeStart":98,"sourceCodeEnd":126,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/Dirt/Controllers/SlackIntegrationController.cs#L98-L126","documentation":"Thrown during the Slack OAuth callback when slackService.ObtainTokenViaOAuth returns null or empty after exchanging the authorization code. This means the token exchange with Slack's API failed — the code was invalid/expired, Slack returned an error, or the network request failed. BadRequestException returns HTTP 400.","triggerScenarios":"OAuth callback with a stale, already-used, or invalid authorization code; Slack API temporarily unavailable during token exchange; redirect URL mismatch between the code request and the token exchange request (Slack validates exact match); client_id/client_secret misconfiguration.","commonSituations":"User takes too long to authorize so the code expires; double-clicking the authorize button consumes the code twice; Slack app credentials rotated but not updated in server config; network blip during the server-to-Slack token call; redirect URI registered in Slack app settings doesn't match the deployed callback URL.","solutions":["Ensure the authorization code is exchanged immediately after receipt (Slack codes expire quickly).","Verify the Slack app's redirect URI in the Slack dashboard exactly matches the deployed callback URL.","Check Slack client_id/client_secret configuration in the server.","Add idempotency handling so a retried callback doesn't re-consume an already-used code."],"exampleFix":"// before\nvar token = await slackService.ObtainTokenViaOAuth(code, callbackUrl);\nif (string.IsNullOrEmpty(token)) throw new BadRequestException(\"Invalid response from Slack.\");\n\n// after — log the actual Slack error for diagnosis\nvar (token, error) = await slackService.ObtainTokenViaOAuthDetailed(code, callbackUrl);\nif (string.IsNullOrEmpty(token))\n{\n    _logger.LogError(\"Slack token exchange failed: {Error}\", error);\n    throw new BadRequestException($\"Slack OAuth failed: {error}\");\n}","handlingStrategy":"retry","validationCode":"// Cannot validate Slack's response client-side, but can pre-check config\nif (string.IsNullOrWhiteSpace(_config[\"Slack:ClientId\"]) ||\n    string.IsNullOrWhiteSpace(_config[\"Slack:ClientSecret\"]))\n    throw new InvalidOperationException(\"Slack OAuth credentials not configured.\");","typeGuard":"public static bool SlackCredentialsConfigured(IConfiguration config) =>\n    !string.IsNullOrEmpty(config[\"Slack:ClientId\"]) &&\n    !string.IsNullOrEmpty(config[\"Slack:ClientSecret\"]);","tryCatchPattern":"int attempts = 0;\nstring? token = null;\nwhile (attempts < 3 && string.IsNullOrEmpty(token))\n{\n    try { token = await _slackService.ObtainTokenViaOAuth(code, callbackUrl); }\n    catch (Exception ex) when (attempts < 2) { _logger.LogWarning(\"Slack token attempt {N} failed: {Ex}\", attempts, ex.Message); }\n    attempts++;\n}\nif (string.IsNullOrEmpty(token))\n    return BadRequest(\"Slack OAuth token exchange failed. Please retry the authorization flow.\");","preventionTips":["Exchange the authorization code immediately — Slack codes expire within minutes.","Register the exact callback URL in the Slack app settings to avoid redirect mismatch.","Keep Slack client_id/client_secret in secure configuration, not in source."],"tags":["slack","oauth","token-exchange","configuration","network","csharp","aspnet"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}