Skip to content

Terraform apply fails with "no OAuth application found with the provided label" even though the Okta app exists

One-sentence summary: the okta_app_oauth data source's label argument runs a limit=1 prefix search sorted by creation date, so an older app whose label starts with yours takes the only result slot and the exact-label check then fails.


🚨 Symptom

The Deploy api_infra (terraform) job fails during 🚀 Terraform Apply:

Error: no OAuth application found with the provided label: clm-api-proc-claim

  with module.saif-appservices.module.external_identity.data.okta_app_oauth.api_app,
  on ../saif-external-identity/main.tf line <n>, in data "okta_app_oauth" "api_app":

The app plainly exists. The auth_ext_okta_client stage in the same build succeeded and created it, and data.okta_auth_server.api_app in the same plan resolves against the same tenant with the same credentials.


📌 Applies to

Aspect Value
Component external-identity module, data.okta_app_oauth.api_app
Forge versions saif-appservices < 3.9.2
Provider okta/okta, all versions through 6.11.0
Environments Production first — non-prod labels carry an -np suffix, which makes the search term more selective

🧠 Cause

This is not a propagation delay, a permissions problem, or a missing app.

The provider implements the singular data source's label argument as a limit=1 query against Okta's List Applications API, then exact-compares only that single result:

req := client.ApplicationAPI.ListApplications(ctx).Limit(1)
if q := filters.GetQ(); q != "" { req = req.Q(q) }
...
if filters.Label != "" && app.GetLabel() != filters.Label {
    return diag.Errorf("no OAuth application found with the provided label: %s", filters.Label)
}

Okta treats q as a case-insensitive startsWith match over both name and label, and returns results sorted by creation date. So if any other app's label begins with yours, and that app was created first, it occupies the one available slot, the exact-label comparison fails, and the lookup errors out.

clm-api-proc-claim hit this because clm-api-proc-claimintake already existed in the same tenant. Whether a given service fails depends on app creation order in the org, not on its Terraform configuration, so a workspace can start failing without anything in the config changing.

Tracked upstream as okta/terraform-provider-okta#2847. The generic okta_app data source was fixed for exactly this bug in #1111/#1115, but the fix was never propagated to okta_app_oauth or okta_app_saml.

Telling this apart from a genuinely missing app

The provider emits two different error strings, and the distinction is the whole diagnosis:

Error text Meaning
no OAuth application found with provided filter: <filters> Zero results came back. The app really is missing, inactive, or in another tenant.
no OAuth application found with the provided label: <label> A result was returned but its label didn't match. The app exists; another app won the slot.

If you see the second form, do not rerun the stage hoping it settles. It is deterministic and will fail again.


✅ Fix

Upgrade the consuming service to saif-appservices 3.9.2 or later, then re-run the deploy:

module "saif-appservices" {
  source  = "app.terraform.io/SAIFCorp/saif-apiservice/azure"
  version = "~> 3.9.2"
}

3.9.2 resolves the API app in two steps instead of one — search with the plural okta_apps data source, which paginates the full result set, filter it for an exact label match, then look the app up by id:

data "okta_apps" "api_app" {
  q           = local.okta_app_label
  active_only = true
}

data "okta_app_oauth" "api_app" {
  id = one(local.api_app_matches)
}

A lifecycle.precondition asserts that exactly one app matched, so a missing or ambiguous app now fails with a message naming the label instead of binding an arbitrary app.

If you need to confirm the collision before upgrading, list the apps Okta actually returns for your label:

$label = "clm-api-proc-claim"
curl -s -H "Authorization: SSWS $env:OKTA_API_TOKEN" `
  "https://saif-external.okta.com/api/v1/apps?q=$label&limit=20" |
  ConvertFrom-Json | Select-Object id, label, created

Any entry whose label starts with yours and has an earlier created timestamp is the app that took the slot.


🔬 Verify

Re-run the failed stage. The plan now reads both data sources and resolves the app by id:

module.saif-appservices.module.external_identity.data.okta_apps.api_app: Refresh complete after 1s
 <= data "okta_app_oauth" "api_app" {
      + id = "0oawz83970ymRPEma5d7"

Confirm that id matches the client_id the auth_ext_okta_client stage logged when it created the app.