{"record":{"id":"0b67c8922868dbf4","repo":"RocketChat/Rocket.Chat","slug":"customoauth-failed-to-extract-avatar-url","errorCode":null,"errorMessage":"CustomOAuth: Failed to extract avatar url","messagePattern":"CustomOAuth: Failed to extract avatar url","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/auth-providers/custom-oauth/customOAuth.ts","lineNumber":181,"sourceCode":"\t\t\t\treturn this.getName(data);\n\t\t\t}\n\n\t\t\treturn value as string;\n\t\t} catch (error) {\n\t\t\tthrow new Error('CustomOAuth: Failed to extract custom name', { cause: error });\n\t\t}\n\t}\n\n\tgetAvatarUrl(data: Record<string, any>) {\n\t\ttry {\n\t\t\tconst value = fromTemplate(this.avatarField, data);\n\n\t\t\tif (!value) {\n\t\t\t\tlogger.debug({ msg: 'Avatar field not found in data', avatarField: this.avatarField, data });\n\t\t\t}\n\t\t\treturn value as string;\n\t\t} catch (error) {\n\t\t\tthrow new Error('CustomOAuth: Failed to extract avatar url', { cause: error });\n\t\t}\n\t}\n\n\tgetName(identity: Record<string, any>): string {\n\t\tconst name = (identity.name ||\n\t\t\tidentity.username ||\n\t\t\tidentity.nickname ||\n\t\t\tidentity.CharacterName ||\n\t\t\tidentity.userName ||\n\t\t\tidentity.preferred_username ||\n\t\t\tidentity.user?.name) as string;\n\t\treturn name;\n\t}\n\n\tnormalizeIdentity(identity: Record<string, any>) {\n\t\tif (identity) {\n\t\t\tfor (const normalizer of Object.values(normalizers)) {\n\t\t\t\tconst result = normalizer(identity);","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/auth-providers/custom-oauth/customOAuth.ts#L163-L199","documentation":"Thrown by CustomOAuthStrategy.getAvatarUrl (the Passport-based custom OAuth strategy) when fromTemplate(this.avatarField, data) throws while reading the avatar URL out of the provider's identity payload. A simply missing avatar value does NOT raise this - that case only logs 'Avatar field not found in data' and returns undefined - so hitting this error means the avatarField template itself is broken, most often an invalid regular expression inside a '{{/regex/::path}}' formula. The error propagates through normalizeIdentity/userProfile and fails the entire OAuth login.","triggerScenarios":"A user completes the OAuth redirect to /_oauth/<name>, the strategy fetches the identity, and avatarField is configured with a template formula whose embedded regex fails new RegExp() (SyntaxError), e.g. avatarField: '{{/[a-z+/::image}}'. Also triggered by calling getAvatarUrl on an instance whose avatarField is undefined (getNestedValue then throws on undefined.split).","commonSituations":"Admin copies a mapping formula from a blog post and the regex never compiles; provider payload rename makes the path portion resolve against a non-object; switching IdPs and reusing the old Avatar Field value; typos in the regex literal form (unbalanced slashes, smart quotes).","solutions":["Set Avatar Field (avatarField) to a plain dot path that exists in the identity payload, e.g. 'picture' or 'user.avatar_url', instead of a regex formula","If you keep a '{{/regex/::path}}' formula, test the regex in isolation (new RegExp('<regex>')) and make sure it is valid and has exactly one capture group","Temporarily clear the Avatar Field setting - avatar extraction is optional and login proceeds without it","Enable debug logging for the CustomOAuth logger and inspect the 'Avatar field not found in data' record to see the real payload shape"],"exampleFix":"// before (Admin -> OAuth -> <custom service> -> Avatar Field)\n{{/^(.+)@/::picture}}   // regex is broken/unbalanced -> SyntaxError -> login fails\n\n// after\npicture                 // plain dot-path into the identity payload","handlingStrategy":"validation","validationCode":"// server-side: verify the configured template resolves against a sample payload before users log in\nimport { fromTemplate } from '../auth-providers/custom-oauth/transform_helpers';\n\nconst sampleIdentity = await fetchIdentityFromProviderOnce(); // GET identityPath with a test token\nconst tpl = settings.get('Accounts_OAuth_Custom-MyIdp_avatarField');\n\ntry {\n  const v = fromTemplate(tpl, sampleIdentity);\n  if (!v) console.warn('avatar template resolves empty; login still works');\n} catch (e) {\n  throw new Error(`avatarField template is broken: ${e.message}`);\n}","typeGuard":"const isResolvableTemplate = (tpl: string, data: Record<string, unknown>): boolean => {\n  try {\n    return fromTemplate(tpl, data) != null;\n  } catch {\n    return false;\n  }\n};","tryCatchPattern":"try {\n  identity.avatarUrl = strategy.getAvatarUrl(identity);\n} catch (error) {\n  logger.warn(`avatar extraction failed for ${strategy.name}: ${error.message}`);\n  identity.avatarUrl = undefined; // avatar is optional; continue the login\n}","preventionTips":["Prefer plain dot-path avatarField values over regex formulas","Test every '{{/regex/::path}}' formula against a real identity payload before saving the OAuth config","Keep avatarField empty when the provider has no avatar claim - extraction is skipped safely","Re-check CustomOAuth debug logs after any provider payload change"],"tags":["oauth","custom-oauth","field-mapping","avatar","regex"],"backgroundTag":"oauth-identity-field-mapping-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}