{"record":{"id":"0194eec7892f6c0d","repo":"jlcodes99/cockpit-tools","slug":"sub2api-access-token-missing","errorCode":"SUB2API_ACCESS_TOKEN_MISSING","errorMessage":"SUB2API_ACCESS_TOKEN_MISSING","messagePattern":"SUB2API_ACCESS_TOKEN_MISSING","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/utils/codexExportFormats.ts","lineNumber":402,"sourceCode":"  };\n\n  if (isCodexApiKeyAccount(account)) {\n    return {\n      ...base,\n      type: 'apikey',\n      credentials: buildSub2apiApiKeyCredentials(account),\n    };\n  }\n\n  if (hasAgentIdentity(account)) {\n    return {\n      ...base,\n      type: 'oauth',\n      credentials: buildSub2apiCredentials(account),\n    };\n  }\n  if (!account.tokens.access_token?.trim()) {\n    throw new Error('SUB2API_ACCESS_TOKEN_MISSING');\n  }\n\n  const credentials = buildSub2apiCredentials(account);\n  const extra = buildSub2apiExtra(account);\n  const item: Sub2apiCreateAccountItem = {\n    ...base,\n    type: 'oauth',\n    credentials,\n    ...(extra ? { extra } : {}),\n  };\n\n  if (!account.tokens.refresh_token?.trim()) {\n    const tokenExpiresAt = resolveAccessTokenExpiry(account);\n    if (!tokenExpiresAt) {\n      throw new Error('SUB2API_ACCESS_TOKEN_EXPIRY_MISSING');\n    }\n    item.expires_at = Math.floor(new Date(tokenExpiresAt).getTime() / 1000);\n    item.auto_pause_on_expired = true;","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/utils/codexExportFormats.ts#L384-L420","documentation":"toSub2apiAccount throws SUB2API_ACCESS_TOKEN_MISSING when an OAuth-type account has no access_token: if the account is not an Agent Identity or API-key account (those paths return earlier with their own credentials), the OAuth fallback path requires tokens.access_token to build the sub2api item.","triggerScenarios":"Calling toSub2apiAccount on an OAuth account whose tokens.access_token is undefined, empty, or whitespace, and which does not match the earlier agentIdentity/API-key branches.","commonSituations":"OAuth flow never completed so tokens were never stored; tokens cleared after a failed refresh; account data partially migrated between versions.","solutions":["Re-run the OAuth login for the account so tokens.access_token is populated","Verify the account's auth type matches its available credentials before export","Skip accounts without access tokens in the export batch","Refresh/re-authorize the account to restore tokens if they expired and were dropped"],"exampleFix":"// before\nconst item = toSub2apiAccount(oauthAccount); // throws\n// after\nif (oauthAccount.tokens?.access_token?.trim()) {\n  const item = toSub2apiAccount(oauthAccount);\n}","handlingStrategy":"validation","validationCode":"if (!account.tokens?.access_token?.trim()) {\n  throw new Error('cannot export: missing access_token');\n}","typeGuard":"function hasAccessToken(a: CodexAccount): boolean {\n  return !!a.tokens?.access_token?.trim();\n}","tryCatchPattern":"try {\n  const item = toSub2apiAccount(account);\n} catch (e) {\n  if ((e as Error).message === 'SUB2API_ACCESS_TOKEN_MISSING') {\n    // re-run OAuth or skip account\n  }\n}","preventionTips":["Complete OAuth before attempting exports","Guard tokens against being cleared on failed refreshes","Pre-flight check all accounts in a batch export and report missing tokens"],"tags":["oauth","access-token","missing-field","export"],"backgroundTag":"missing-credential-field","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}