Skip to content

@run402/sdk doc claims type guards (isPaymentRequired, isProjectNotFound, etc.) and an e.kind field — neither exists at runtime #178

@MajorTal

Description

@MajorTal

Summary

https://run402.com/llms-sdk.txt and the SDK README recommend branching on errors via either:

The SDK exports type guards for "branching with the exported type guards (or by comparing e.kind)" rather than instanceof checks:

  • isPaymentRequired(e)
  • isProjectNotFound(e)
  • isUnauthorized(e)
  • isRetryableRun402Error(e)

Neither the type guard functions nor the kind property exist in @run402/sdk@1.53.0. Following either documented pattern produces silent wrong-branch behavior or a TypeError.

Repro — type guards are missing

import * as sdk from \"@run402/sdk\";

console.log(typeof sdk.isPaymentRequired);   // undefined
console.log(typeof sdk.isProjectNotFound);   // undefined
console.log(typeof sdk.isUnauthorized);      // undefined
console.log(typeof sdk.isRetryableRun402Error); // undefined

console.log(Object.keys(sdk).sort());
// → [ 'ApiError','Deploy','LocalError','NetworkError','PaymentRequired',
//     'ProjectNotFound','Run402','Run402DeployError','Run402Error',
//     'ScopedRun402','Unauthorized','files','run402' ]

Only the classes are exported. None of the recommended type guard helpers are.

Repro — e.kind is undefined on every error class

import { run402 } from \"@run402/sdk/node\";
const r = run402();
try { await r.projects.info(\"nonexistent_proj_zzz\"); }
catch (e) {
  console.log(\"name:\", e.name);                  // ProjectNotFound
  console.log(\"kind:\", e.kind);                  // undefined
  console.log(\"keys:\", Object.getOwnPropertyNames(e));
  // → [ 'stack','message','status','body','context','code','category',
  //     'retryable','safeToRetry','mutationState','traceId','details',
  //     'nextActions','name','projectId' ]
}

The error has code, category, name — but no kind. So the doc's two suggested patterns both fail:

  1. isProjectNotFound(e)TypeError: isProjectNotFound is not a function
  2. e.kind === \"project_not_found\" → always false

Working pattern is the one the doc says to avoid (e instanceof ProjectNotFound), so anyone following the docs ends up with branching that silently never matches.

Why this matters

The doc explicitly steers consumers away from instanceof — likely because it isn't reliable across realms / dual-loaded SDK copies. But there's no fallback that actually works. This is the kind of quiet bug that sleeps until a user adds a fast-path for "the wallet ran out of credit" and the path silently never fires.

Suggested fix

Pick one of:

  1. Export the documented type guards in index.ts:

    export const isPaymentRequired = (e) => e?.code === \"PAYMENT_REQUIRED\" || e?.constructor?.name === \"PaymentRequired\";
    export const isProjectNotFound = (e) => e?.code === \"PROJECT_NOT_FOUND\" || e?.constructor?.name === \"ProjectNotFound\";
    export const isUnauthorized   = (e) => e?.constructor?.name === \"Unauthorized\";
    export const isRetryableRun402Error = (e) => e?.retryable === true;
  2. Add a stable kind discriminator on Run402Error.prototype (\"payment_required\", \"project_not_found\", \"unauthorized\", \"network\", \"local\", \"deploy\", \"api\").

  3. Or update the docs to match what's actually exported and recommend e instanceof ProjectNotFound (the only thing that currently works).

I'd vote for #1 + #2 since the doc claims both already exist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions