Skip to main content
Every SDK method throws a subclass of IMessageError on failure. Use instanceof to branch on the specific error type - no boolean getters, no magic strings.

Error hierarchy

IMessageError
├── AuthenticationError   token rejected or expired
├── NotFoundError         chat, message, or attachment does not exist
├── RateLimitError        server is throttling requests
├── ValidationError       bad input - wrong types, missing fields
└── ConnectionError       network or gRPC transport failure
gRPC status code mapping
gRPC statusSDK error class
UNAUTHENTICATED, PERMISSION_DENIEDAuthenticationError
NOT_FOUNDNotFoundError
RESOURCE_EXHAUSTEDRateLimitError
INVALID_ARGUMENT, FAILED_PRECONDITIONValidationError
UNAVAILABLE, DEADLINE_EXCEEDEDConnectionError
All othersIMessageError (base class)

Basic usage

import {
  AuthenticationError,
  ConnectionError,
  NotFoundError,
  RateLimitError,
  ValidationError,
} from "@photon-ai/advanced-imessage";

try {
  await im.messages.send(chat, "Hello!");
} catch (err) {
  if (err instanceof RateLimitError) {
    // back off and retry
    await sleep(1000);
  } else if (err instanceof NotFoundError) {
    // chat does not exist
    console.error("Chat not found:", chat);
  } else if (err instanceof AuthenticationError) {
    // refresh the token
    token = await refreshToken();
  } else if (err instanceof ValidationError) {
    // bad input, do not retry
    console.error("Invalid request:", err.message);
  } else if (err instanceof ConnectionError) {
    // transient transport failure
    console.error("Connection lost:", err.message);
  } else {
    throw err;
  }
}

IMessageError properties

All error subclasses inherit:
class IMessageError extends Error {
  readonly code: ErrorCode; // SDK-level error code enum
  readonly retryable: boolean; // whether retrying the request may succeed
  readonly grpcCode: number; // underlying gRPC status code
  readonly context: Record<string, string>; // extra debug info from gRPC trailing metadata
}
The retryable flag is read from the server’s gRPC trailing metadata (x-retryable header), so it is determined by the server on a per-response basis. By convention, RateLimitError and ConnectionError are typically retryable, but the server may override this for any error type.

Error codes

ErrorCode has 22 values across 6 categories:
CategoryError codes
Authenticationunauthenticated, tokenExpired, tokenBlocked, unauthorized
Rate limitingdailyLimitExceeded, recipientLimitExceeded
DeduplicationduplicateMessage
Not foundchatNotFound, messageNotFound, attachmentNotFound, addressNotFound, scheduledMessageNotFound, pollNotFound
ValidationinvalidArgument, preconditionFailed, operationNotSupported, privateApiUnavailable
InfrastructureserviceUnavailable, timeout, internalError, databaseError, networkError
import { ErrorCode } from "@photon-ai/advanced-imessage";

if (err instanceof IMessageError && err.code === ErrorCode.chatNotFound) {
  // handle specifically
}

Retrying with the SDK

Set retry: true (or a RetryOptions object) in createClient to let the SDK handle transient retries automatically:
const im = createClient({
  address: "your-instance.imsg.photon.codes:443",
  token: "...",
  retry: {
    maxAttempts: 3,
    initialDelay: 200,
    maxDelay: 5000,
  },
});
When retry is enabled, ConnectionError and RateLimitError are retried transparently before the error propagates to your code.

Escape hatch: _raw

Most domain types carry _raw containing the raw gRPC response for debugging purposes. The types that include _raw are Chat, Message, AttachmentInfo, and AddressInfo. Other types such as ScheduledMessage, FindMyFriend, and PollInfo do not include _raw.
try {
  await im.messages.send(chat, "Hello!");
} catch (err) {
  if (err instanceof IMessageError) {
    console.log(err.code, err.grpcCode, err.retryable, err.context);
  }
}