← back to Goldleafwallpaper

scripts/ig-poster/classify-error.js

49 lines

'use strict';
/**
 * classify-error.js — classify a failed IG Graph publish error message.
 *
 * WHY THIS EXISTS (TK-11880 grooming cycle 2, 2026-09-17):
 * Meta's Graph API stamps MANY non-auth failures with `type:"OAuthException"`.
 * The poster's old inline test keyed the "token expired — re-auth" alert off the
 * bare substring `OAuth`, so a MEDIA-FETCH rejection (code 9004 / subcode 2207052,
 * "The media could not be fetched from this URI") was misclassified as a dead token
 * and fired a false re-auth alert — on a token that meta-token-canary independently
 * reads as HEALTHY (verified 2026-09-17). That is the CLAUDE.md TK-11431 "a check
 * reported the wrong cause" class: it would send someone to rotate a healthy
 * credential while the REAL problem (an image URL IG can't fetch/accept) stays hidden.
 *
 * The fix: recognize the media-error signature explicitly and EXCLUDE it from the
 * auth test, while keeping genuine expiry detection (Meta always carries code 190 +
 * a canonical "Session has expired" / "access token" phrase on a real expiry).
 *
 * Pure + side-effect free so it can be unit-tested without running the poster
 * (the poster would otherwise attempt a live post). Ship the testability seam.
 */

// IG media-fetch / media-type rejections. NOT a token problem — the token is fine;
// IG could not fetch or accept the image URL (dimensions, format, or transform params).
const MEDIA_ERR_RE =
  /"code":\s*9004|"error_subcode":\s*2207052|media could not be fetched|Only photo or video can be accepted/i;

// Genuine access-token failures. Meta emits code 190 (+ subcodes 458/463/467/492)
// with a canonical "Session has expired" / "access token" phrase on a real expiry.
// Deliberately does NOT match the bare type string "OAuthException" alone, because
// media (9004) and rate-limit (4/17/32/613) errors also carry that type.
const AUTH_ERR_RE =
  /"code":\s*190|"error_subcode":\s*(?:458|463|467|492)|access token|session has expired|not a valid access token|token is invalid/i;

/**
 * @param {string} msg  the error message (typically the JSON body Meta returned)
 * @returns {{isMediaErr:boolean, isAuthErr:boolean}}
 *   isAuthErr is only true when it is NOT a media error, so a code-9004 rejection
 *   can never route to the re-auth alert.
 */
function classifyPostError(msg) {
  const s = String(msg == null ? '' : msg);
  const isMediaErr = MEDIA_ERR_RE.test(s);
  const isAuthErr = !isMediaErr && AUTH_ERR_RE.test(s);
  return { isMediaErr, isAuthErr };
}

module.exports = { classifyPostError, MEDIA_ERR_RE, AUTH_ERR_RE };