Skip to content

fix(utils): make hasErrorCode safe for nullish catch bindings - #2014

Closed
zunixport wants to merge 1 commit into
ProjectOpenSea:mainfrom
zunixport:fix/has-error-code-nullish-guard
Closed

zunixport wants to merge 1 commit into
ProjectOpenSea:mainfrom
zunixport:fix/has-error-code-nullish-guard

Conversation

@zunixport

Copy link
Copy Markdown
Contributor

hasErrorCode throws on null / undefined

hasErrorCode is a type guard declared over unknown:

export const hasErrorCode = (error: unknown): error is ErrorWithCode => {
  const untypedError = error as Partial<ErrorWithCode>
  return !!untypedError.code
}

unknown includes null and undefined, but the implementation casts the argument and reads .code off it immediately. On a nullish value that throws TypeError: Cannot read properties of null (reading 'code') — the guard crashes on exactly the inputs it exists to handle.

Why it matters

Both values reach a catch (error) binding in ordinary code: throw null, or Promise.reject() with no reason. hasErrorCode is used that way in FulfillmentManager.isOrderFulfillable:

} catch (error) {
  if (hasErrorCode(error) && error.code === "CALL_EXCEPTION") {
    return false
  }
  throw error
}

The intent there is "inspect the code, otherwise rethrow as-is". For a nullish throw, the guard itself throws, so the caller receives an unrelated TypeError that says nothing about what actually failed, and the throw error fallback is skipped. The helper is also exported from the package root, so any consumer calling it according to its (error: unknown) signature hits the same crash.

Fix

Return false for null / undefined before reading the property. Truthiness of code is unchanged for every other input.

Tests

Extended test/sdk/misc.spec.ts (already covers other exports of src/utils/utils.ts) with a hasErrorCode block: true for an error carrying a code, false for a codeless one, and an explicit "does not throw" assertion for null / undefined.

@ryanio

ryanio commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Thanks @zunixport. This repo is a read-only mirror, so we've recreated the fix in our internal monorepo with you credited as co-author. It ships in the upcoming @opensea/sdk 12.11.1.

On impact: the only first-party caller is isOrderFulfillable, and ethers doesn't reject with a nullish value, so in practice this affects code that calls the exported helper directly. It's still worth fixing, since the signature accepts unknown.

Closing this PR since the change now lives in the monorepo. Thanks!

@ryanio ryanio closed this Sep 30, 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.

2 participants