fix: catch feature_not_enabled via err.body.errorCode and 400 status in organizations handler - #1482
Conversation
…in organizations handler
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1482 +/- ##
=======================================
Coverage 80.81% 80.82%
=======================================
Files 163 163
Lines 7805 7809 +4
Branches 1741 1745 +4
=======================================
+ Hits 6308 6312 +4
Misses 797 797
Partials 700 700 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| // not `err.errorCode`. Some tenants also surface `feature_not_enabled` as a 400 rather | ||
| // than a 403, so we match on the error code regardless of the HTTP status. | ||
| function isFeatureNotEnabled(err): boolean { | ||
| return err?.statusCode === 403 || err?.body?.errorCode === 'feature_not_enabled'; |
There was a problem hiding this comment.
The diagnosis on err.body.errorCode is spot on. One thing I wanted to check with you, the first branch
err?.statusCode === 403 returns true for any 403 regardless of what the error code is. So a 403 with errorCode: 'insufficient_scope' (M2M app missing a required scope) would
also be caught here and logged as "Org-to-app entitlement is not enabled", which could send the operator down the wrong path
I know this broad 403 catch was already there before this PR, so not something you introduced. But since we're refactoring this into a helper anyway, would it make sense to tighten it here?
Since err?.body?.errorCode === 'feature_not_enabled' already handles both 400 and 403 carrying the right error
code, the status check seems redundant. Something like:
function isFeatureNotEnabled(err): boolean {
return err?.body?.errorCode === 'feature_not_enabled';
}
Or if you'd prefer to be explicit about which status codes are expected:
function isFeatureNotEnabled(err): boolean {
return (err?.statusCode === 400 || err?.statusCode === 403)
&& err?.body?.errorCode === 'feature_not_enabled';
}
Happy to hear your thoughts on this, maybe there's a reason to keep the status only check that I'm missing?
There was a problem hiding this comment.
Addressed. Tightened away from the raw 403 assumption. The log now reports the actual error code instead of always saying the feature is off, so a 403 insufficient_scope no longer gets mislabeled.
There was a problem hiding this comment.
Quick question on the existing test mock,
err.errorCode = 'feature_not_enabled' is still being set here, but isFeatureNotEnabled now reads from
err?.body?.errorCode and never touches err.errorCode. So this line looks like it's doing nothing in the updated implementation, and the test passes only because statusCode === 403 fires.
Would it make sense to update this mock to reflect the correct SDK shape, so the test is actually validating the path it claims to test?
// instead of:
err.errorCode = 'feature_not_enabled';
// something like:
err.body = { errorCode: 'feature_not_enabled' };
There was a problem hiding this comment.
You're right.. Updated the mock to err.body = { errorCode: 'feature_not_enabled' } so it validates the real SDK path instead of passing only on statusCode === 403.
| // The auth0 SDK's ManagementError exposes the API error code on `err.body.errorCode`, | ||
| // not `err.errorCode`. Some tenants also surface `feature_not_enabled` as a 400 rather | ||
| // than a 403, so we match on the error code regardless of the HTTP status. | ||
| function isFeatureNotEnabled(err): boolean { |
There was a problem hiding this comment.
Minor one: would you be okay adding an explicit type to the err parameter in the helper signature? Right now it's implicitly any, and the project uses strict TypeScript. Other catch blocks in this file use catch (err: any), so something like function isFeatureNotEnabled(err: any): boolean would stay consistent with the existing style
There was a problem hiding this comment.
Addressed. Helper signature now uses err: any, consistent with the other catch blocks.
🔧 Changes
Organization export/import could fail on tenants without the org-to-app or discovery-domains entitlement. The handler tried to swallow the
feature_not_enablederror but the guard was wrong in two ways:err.errorCode, which is alwaysundefined. The auth0 SDK v6.3.0ManagementErrorexposes the API code aterr.body.errorCode, noterr.errorCode.403. Some tenants now return400forfeature_not_enabled, so the check missed those entirely.With both conditions failing, the error was rethrown and aborted the run during
processChanges(which callsgetTypefirst).This adds a small
isFeatureNotEnabled(err)helper that reads the code fromerr.body.errorCodeand matches it regardless of HTTP status (400 or 403). Both catch blocks in the organizations handler (org-client associations and discovery domains) now use it and skip the unavailable data gracefully with a debug log instead of failing.No data-shape changes: no config schema, JSON, or YAML output formats were modified. Behavior change is limited to error handling on unentitled tenants.
🔬 Testing
400whose code lives onerr.body.errorCode. It fails against the previous code and passes with the fix.tscandeslintclean.export, read-only) that has organizations and does not have the org-to-app entitlement. Before the fix this export threwfeature_not_enabled; after the fix it completes with exit 0, logs "Org-to-app entitlement is not enabled for this tenant. Skipping org-client associations." once per org, and still exports the organizations.📝 Checklist