Skip to content

fix: catch feature_not_enabled via err.body.errorCode and 400 status in organizations handler - #1482

Merged
harshithRai merged 5 commits into
masterfrom
DXCDT-2295
Sep 9, 2026
Merged

fix: catch feature_not_enabled via err.body.errorCode and 400 status in organizations handler#1482
harshithRai merged 5 commits into
masterfrom
DXCDT-2295

Conversation

@harshithRai

Copy link
Copy Markdown
Contributor

🔧 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_enabled error but the guard was wrong in two ways:

  • It read the error code from err.errorCode, which is always undefined. The auth0 SDK v6.3.0 ManagementError exposes the API code at err.body.errorCode, not err.errorCode.
  • It only matched HTTP 403. Some tenants now return 400 for feature_not_enabled, so the check missed those entirely.

With both conditions failing, the error was rethrown and aborted the run during processChanges (which calls getType first).

This adds a small isFeatureNotEnabled(err) helper that reads the code from err.body.errorCode and 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

  • Added a unit test reproducing the reported failure: a 400 whose code lives on err.body.errorCode. It fails against the previous code and passes with the fix.
  • Ran the full non-e2e suite in chunks: 654 handler tests, plus context, tools, root, and command suites all pass; tsc and eslint clean.
  • Verified live against a dev tenant (export, read-only) that has organizations and does not have the org-to-app entitlement. Before the fix this export threw feature_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

  • All new/changed/fixed functionality is covered by tests (or N/A)
  • I have added documentation for all new/changed functionality (or N/A)

@codecov-commenter

codecov-commenter commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.82%. Comparing base (4955e4b) to head (154b9c4).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
src/tools/auth0/handlers/organizations.ts 20.00% 2 Missing and 2 partials ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@harshithRai
harshithRai marked this pull request as ready for review September 3, 2026 07:43
@harshithRai
harshithRai requested a review from a team as a code owner September 3, 2026 07:43
// 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';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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' };

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed. Helper signature now uses err: any, consistent with the other catch blocks.

@harshithRai
harshithRai merged commit 884e253 into master Sep 9, 2026
9 checks passed
@harshithRai
harshithRai deleted the DXCDT-2295 branch September 9, 2026 14:14
@harshithRai harshithRai mentioned this pull request Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants