Skip to content

sdk publish: auth failures from the publishing API print "Unexpected error" / bare "Unauthorized access." instead of the apimatic auth login hint #322

Description

@mrafnadeem-apimatic

Summary

When apimatic sdk publish gets an auth failure from the publishing API, the CLI prints a message with no remedy in it — either the bare Unauthorized access. (401) or the generic An unexpected error occurred, please try again later… (403). Neither names apimatic auth login, so a user whose auth key has expired or been revoked has nothing to act on.

The 403 case is the worse of the two: an authorization failure is reported as an unexpected error, which reads like an APIMatic outage and points the user at support instead of at re-authenticating.

Reproduction

Build the CLI, then point it at a config with a rejected key:

pnpm build
mkdir -p /tmp/cfg && echo '{"email":"a@b.com","authKey":"bogus-invalid-key"}' > /tmp/cfg/config.json
cp -r test/resources/build-inputs/default /tmp/repro/src

APIMATIC_CONFIG_DIR=/tmp/cfg node ./bin/run.js sdk publish \
  --input /tmp/repro --destination /tmp/repro/sdk --language typescript \
  --profile-id a1b2c3d4e5f6a1b2c3d4e5f6 --version 1.0.0 --publish-type package --force

Actual (401 — verified against the live api.package-publishing.apimatic.io, which answers a bad key with 401 {"title":"Unauthorized","detail":"You are not authorized to access this resource"}):

■  Failed to search for publishing profile.
■  Unauthorized access.
└  Failed

Actual (403 — reproduced by pointing APIMATIC_BASE_URL's third segment at a local server returning 403 with a ProblemDetails body):

■  Failed to search for publishing profile.
■  An unexpected error occurred, please try again later. If the problem persists, please reach out to our team at 'support@apimatic.io'
└  Failed

Expected, matching what the SDK-generation step already prints for the same rejected key (apimatic sdk generate --auth-key bogus-invalid-key):

■  Authorization has been denied for this request. Please run apimatic auth login to log in via browser,
   or provide a valid auth key using the apimatic auth login --auth-key

Cause

PublishingApiService routes every caught error through handleServiceError (src/infrastructure/services/publishing-api-service.ts:42, :102, :129), and that function is the one mapper that does not apply the auth hint:

  • handleServiceError maps an axios 401 onto the bare ServiceError.UnAuthorized singleton ("Unauthorized access."), not ServiceError.unauthorizedWithHint(...)src/infrastructure/service-error.ts:169.
  • It has no 403 branch at all for axios errors, so a 403 falls through to ServiceError.ServerError, whose message is "An unexpected error occurred…" (src/infrastructure/service-error.ts:18, :178).

Both gaps are already fixed in the wrappers that exist for exactly this purpose — mapTransportError (401 → hint) and mapRequestError (ProblemDetails 400/403 → the API's own title/detail). PluginService uses them (src/infrastructure/services/plugin-service.ts:94, :134, :154); PublishingApiService does not.

#300 (a1ab840) fixed only the local pre-check in this service — the "no key in config.json" branch, which correctly returns unauthorizedWithHint(null). A user who has a key that the server rejects still takes the wire path, which was left on handleServiceError. That's why logging out gives a good message but an expired key does not.

Suggested fix

Swap the three handleServiceError calls in PublishingApiService for mapRequestError, which handles ProblemDetails 400/403 bodies and the 401 hint in one step. The hand-rolled error.status === 400 / 403 / 404 block in publishSdkPackage (:85:100) then becomes largely redundant — and it is fragile anyway: it dereferences data.errors / data.title without checking that data is an object, so an error response with an empty or non-JSON body throws a TypeError out of the catch.

Longer term, handleServiceError itself should stop being the odd mapper out — the two "correct" behaviours living in wrappers around it means every new call site has a 50/50 chance of picking the one that drops the remedy.

Related, same flow

pollPublishingStatus discards the ServiceError entirely — a 401 while polling getSdkPublishingLog prints only Failed to fetch publishing status. (src/prompts/sdk/publish.ts:60-62). Fixing the mapper above will not surface anything there until the prompt logs publishingLogResult.error.errorMessage.

Also worth a look: the SDK/portal generation status pollers (getSdkGenerationStatus, getV4SdkGenerationStatus, getPortalGenerationStatus in portal-service.ts) classify 401 as the same bare ServiceError.UnAuthorized, and everything unrecognised as ServiceError.InvalidResponse — which carries that identical "An unexpected error occurred…" text. The status endpoints do answer a rejected key with 401 (verified), so a key that expires mid-generation lands on the bare message too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions