Skip to content

@W-24017820@ fix(package-convert): actionable error when Dev Hub lacks 2GP - #931

Open
agayakwad-salesforce wants to merge 1 commit into
mainfrom
t/2gp-readiness/w-24017820/actionable-error-for-convert-without-2gp
Open

agayakwad-salesforce wants to merge 1 commit into
mainfrom
t/2gp-readiness/w-24017820/actionable-error-for-convert-without-2gp

Conversation

@agayakwad-salesforce

@agayakwad-salesforce agayakwad-salesforce commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

@W-24017820@ Actionable error when a Dev Hub lacks Second-Generation Packaging

TL;DR

When a user runs sf package convert or sf package create against a Dev Hub
that does not have Second-Generation Managed Packages enabled, the CLI used to
fail with a raw, unexplained server error and zero guidance. This PR replaces those
cryptic errors with a clear, actionable message that names the real cause and tells the
user exactly what to do.

Command Before (error surfaced) After (error surfaced)
sf package convert INVALID_TYPE: sObject type 'Package2' is not supported. … Can't convert package. The Dev Hub you specified doesn't have the Second-Generation Managed Packages setting enabled. Enable this setting on your Dev Hub, and try again.
sf package create NOT_FOUND: The requested resource does not exist Can't create package. The Dev Hub you specified doesn't have the Second-Generation Managed Packages setting enabled. Enable this setting on your Dev Hub, and try again.

1. The problem

The 2GP packaging commands assume the target Dev Hub has the Second-Generation
Managed Packages
setting enabled. When it is not enabled, the Package2 entity
does not exist on that org, and the very first server call in each command fails.

Because those first calls had no error handling for this condition, the raw
tooling-API error propagated straight to the user:

  • sf package convert
    INVALID_TYPE: sObject type 'Package2' is not supported. If you are attempting to use a
    custom object, be sure to append the '__c' after the entity name. Please reference your
    WSDL or the describe call for the appropriate names.
    
  • sf package create
    NOT_FOUND: The requested resource does not exist
    

Neither message mentions 2GP, the Dev Hub, or any next step. A user has no way to know
the real problem is simply "2GP isn't enabled on this Dev Hub."


2. Why it happens (root cause)

The two commands reach Package2 through two different tooling APIs, which is why
they fail with two different raw errors:

sf package convert — via a SOQL query

findOrCreatePackage2 (src/package/packageConvert.ts) runs:

const query = `SELECT Id, Name FROM Package2 WHERE ConvertedFromPackageId = '${seedPackage}'`;
const queryResult = (await connection.tooling.query(query)).records; // <-- unguarded

When Package2 is unknown, the SOQL parser rejects the query with
INVALID_TYPE: sObject type 'Package2' is not supported.

sf package create — via a REST sObject call

createPackage (src/package/packageCreate.ts) runs:

const createResult = await connection.tooling.sobject('Package2').create(request);

When Package2 is unknown, the tooling REST endpoint /sobjects/Package2 does not
exist, so it 404s with NOT_FOUND: The requested resource does not exist.

A subtle secondary bug in package create

packageCreate.ts already routed its .catch through applyErrorAction /
massageErrorMessage in packageUtils.ts. applyErrorAction does have a
NOT_FOUND branch — but it only appends an action (packageNotEnabledAction); it
never rewrites the headline message. Worse, that action is then silently dropped by
SfError.wrap(), whose fromBasicError helper copies only message, name, and
cause onto the new SfError — not actions. Net effect: the user saw the bare
NOT_FOUND with no message improvement and no action hint (actions: None).


3. How to reproduce the "before" behaviour

Prerequisites (local Core dev environment used for verification):

  • A Dev Hub org that does not have Second-Generation Managed Packages enabled.
  • A 1GP managed package (033…) for the convert path.
  • An sfdx-project.json with a valid namespace.

Reproduce convert:

sf package convert \
  --package 033xx0000004GUX \
  --installation-key-bypass \
  --target-dev-hub <no-2gp-devhub> \
  --wait 5
# => Error (INVALID_TYPE): sObject type 'Package2' is not supported. …

Reproduce create:

sf package create \
  --name RealTestPkg \
  --package-type Managed \
  --path force-app \
  --target-dev-hub <no-2gp-devhub>
# => Error (NOT_FOUND): The requested resource does not exist

Confirm the underlying condition directly:

sf data query --use-tooling-api --query "SELECT Id FROM Package2 LIMIT 1" --target-org <no-2gp-devhub>
# => sObject type 'Package2' is not supported. …

4. What the fix changes

New user-facing messages

  • messages/package_version_create.mdconvertPackagingNotEnabledOnOrg

    Can't convert package. The Dev Hub you specified doesn't have the Second-Generation Managed Packages setting enabled. Enable this setting on your Dev Hub, and try again.

  • messages/package_create.mdcreatePackagingNotEnabledOnOrg

    Can't create package. The Dev Hub you specified doesn't have the Second-Generation Managed Packages setting enabled. Enable this setting on your Dev Hub, and try again.

src/package/packageConvert.ts (convert path)

  • Wrap the Package2 SOQL query in findOrCreatePackage2 in a try/catch. If the
    error is the "not supported" condition, throw convertPackagingNotEnabledOnOrg;
    otherwise rethrow unchanged.
  • Guard the Package2 create path — if createResult.errors reports the same
    condition, throw the actionable message instead of the generic combineSaveErrors
    output.
  • Add helper isPackage2NotSupportedError(err) — matches the SOQL error on a
    substring (sObject type 'Package2' is not supported.) because the full server
    message may append custom-object WSDL boilerplate. Handles both Error instances and
    jsforce SaveError plain objects.

src/package/packageCreate.ts (create path)

  • Load the package_create message bundle.
  • In createPackage, intercept before the massageErrorMessage / SfError.wrap
    pipeline: if the error is the 2GP-not-enabled condition, throw
    createPackagingNotEnabledOnOrg. Also guard the createResult.errors path.
  • Add helper isPackagingNotEnabledError(err) — for this path the signal is the REST
    404, so it matches name === 'NOT_FOUND' and message
    The requested resource does not exist. It reads errorCode/statusCode from
    jsforce SaveError objects as well as Error instances.
    • Note: the SOQL "not supported" substring is intentionally not matched here —
      package create never issues a SOQL query, so that form cannot occur on this path.

Tests

  • test/package/packageConvert.test.ts — cases asserting the actionable message on both
    the query-throws and create-result paths, and that the raw INVALID_TYPE text does not
    leak.
  • test/package/packageCreate.test.ts — new createPackage 2GP-not-enabled handling
    block: (1) NOT_FOUND thrown by the create call, (2) NOT_FOUND reported in
    createResult.errors, (3) unrelated errors rethrown unchanged.

5. Testing done (after-behaviour validation)

Automated

yarn build                                   # compile + lint: clean
yarn mocha test/package/packageCreate.test.ts   # 9 passing
yarn mocha test/package/packageConvert.test.ts  # 25 passing

Manual, end-to-end against a real no-2GP Dev Hub (local Core)

Scenario Command Target org Result
Convert, fix path sf package convert Dev Hub / no 2GP ConvertPackagingNotEnabledOnOrgError + actionable message
Create, fix path sf package create Dev Hub / no 2GP CreatePackagingNotEnabledOnOrgError + actionable message
Over-trigger guard sf package create non-Dev-Hub org still NotADevHubError (flag-parse gate) — fix does not false-positive
No regression sf package convert non-Dev-Hub org still NotADevHubError

Exact "after" output — sf package create on a no-2GP Dev Hub:

Error (CreatePackagingNotEnabledOnOrgError): Can't create package. The Dev Hub you specified
doesn't have the Second-Generation Managed Packages setting enabled. Enable this setting on
your Dev Hub, and try again.

JSON: name/code = CreatePackagingNotEnabledOnOrgError, exitCode = 1.


6. How a reviewer can test this fix

  1. Have a Dev Hub without 2GP enabled (do not enable Second-Generation Managed
    Packages on it) and authenticate the CLI to it.
  2. Build & link this branch locally so the CLI runs the compiled lib:
    yarn install && yarn build
    sf plugins link .        # or link via plugin-packaging
  3. Run the automated tests:
    yarn mocha test/package/packageConvert.test.ts   # expect 25 passing
    yarn mocha test/package/packageCreate.test.ts    # expect 9 passing
  4. Exercise package create in an sfdx project directory:
    sf package create --name TestPkg --package-type Managed --path force-app \
      --target-dev-hub <no-2gp-devhub>
    Expect: CreatePackagingNotEnabledOnOrgError with the actionable message (not NOT_FOUND).
  5. Exercise package convert with a 1GP 033… package id:
    sf package convert --package <033id> --installation-key-bypass \
      --target-dev-hub <no-2gp-devhub> --wait 5
    Expect: ConvertPackagingNotEnabledOnOrgError with the actionable message (not INVALID_TYPE).
  6. Negative / guard check — point the same commands at a non-Dev-Hub org and
    confirm you still get NotADevHubError (the fix must not mask that).
  7. (Optional) Confirm the underlying condition independently:
    sf data query --use-tooling-api --query "SELECT Id FROM Package2 LIMIT 1" \
      --target-org <no-2gp-devhub>
    # => sObject type 'Package2' is not supported.

7. Notes, scope, and follow-ups

  • Exit code is unchanged (1). This is purely a messaging improvement; automation
    keying off exit status is unaffected.
  • Guidance lives in the message text, not a separate CLI actions block (consistent
    with the existing packageVersionRetrieve handling). If we prefer a formal "Try this:"
    action hint, that's a small follow-up.
  • sf package version create is intentionally out of scope. On a no-2GP Dev Hub it
    fails earlier with ErrorNoIdInHubError because it requires a real 0Ho package id
    that cannot exist without 2GP (chicken-and-egg). There is no faithful "real 0Ho +
    no 2GP" state to handle, so it was left as-is.
  • Latent bug flagged for a separate PR: SfError.wrap's fromBasicError drops
    .actions, so any action added by applyErrorAction in the create .catch path is
    currently discarded. This fix sidesteps it; a dedicated change should address the
    general case.

Comment thread messages/package_version_create.md Outdated
@agayakwad-salesforce
agayakwad-salesforce force-pushed the t/2gp-readiness/w-24017820/actionable-error-for-convert-without-2gp branch from d5ec8f1 to fd58621 Compare September 11, 2026 23:10
… 2GP

When running sf package convert against a Dev Hub without second-generation
managed packaging enabled, the Package2 tooling query in findOrCreatePackage2
threw a raw INVALID_TYPE "sObject type 'Package2' is not supported" error with
no actionable guidance.

Wrap the Package2 query and the Package2 create path so the "not supported"
error is surfaced as a clear, actionable message (convertPackagingNotEnabledOnOrg),
consistent with the existing handling in package version retrieve.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@agayakwad-salesforce
agayakwad-salesforce force-pushed the t/2gp-readiness/w-24017820/actionable-error-for-convert-without-2gp branch from fd58621 to 45ee002 Compare September 16, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants