{"record":{"id":"113c24428137d9e4","repo":"TryGhost/Ghost","slug":"the-url-must-be-in-a-format-like-https-www-threa","errorCode":null,"errorMessage":"The URL must be in a format like https://www.threads.net/@yourUsername","messagePattern":"The URL must be in a format like https://www\\.threads\\.net/@yourUsername","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"apps/admin/src/settings/app/utils/social-urls/threads.ts","lineNumber":16,"sourceCode":"import {INSTAGRAM_USERNAME_RULE} from './instagram';\nimport {createPlatformValidator} from './platform-validator';\n\n// Threads accounts are Instagram accounts, so the username rule is shared.\n// threads.com URLs are accepted and canonicalised to www.threads.net.\nconst threads = createPlatformValidator({\n    domains: ['threads.net', 'threads.com'],\n    www: true,\n    pathTypes: [\n        // /@username is the canonical form, but /username also resolves (it\n        // redirects on threads.net), so URLs without the @ are accepted too\n        {urlPrefix: '@', storagePrefix: '@', rule: INSTAGRAM_USERNAME_RULE},\n        {urlPrefix: '', storagePrefix: '@', rule: INSTAGRAM_USERNAME_RULE}\n    ],\n    errors: {\n        invalidUrl: 'The URL must be in a format like https://www.threads.net/@yourUsername',\n        invalidUsername: 'The URL must be in a format like https://www.threads.net/@yourUsername'\n    }\n});\n\nexport const validateThreadsUrl = threads.validate;\nexport const threadsHandleToUrl = threads.handleToUrl;\nexport const threadsUrlToHandle = threads.urlToHandle;\n","sourceCodeStart":1,"sourceCodeEnd":24,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/apps/admin/src/settings/app/utils/social-urls/threads.ts#L1-L24","documentation":"Threads reuses the Instagram username rule (ASCII letters, numbers, underscores, periods; 1-30 chars; no leading/trailing/consecutive periods). Both the invalidUrl and invalidUsername error strings resolve to this same message. The error fires when the path segment after the domain and optional '@' prefix does not satisfy the INSTAGRAM_USERNAME_RULE, or when the input does not parse as a threads.net/threads.com URL at all.","triggerScenarios":"Calling validateThreadsUrl, threadsHandleToUrl, or threadsUrlToHandle with a URL whose username contains non-ASCII characters, exceeds 30 characters, has leading/trailing/consecutive periods, or contains disallowed characters. Also fires as invalidUrl when the domain is not threads.net or threads.com, or the path is empty after the domain.","commonSituations":"User pastes a threads.net URL but the handle has an underscore-period combo like '.name' (leading period rejected by notAtBoundary), or a 35-character handle. A common paste mistake is including the full thread URL to a specific post (threads.net/@user/post/123) — the pipeline takes only the first segment so this usually works, but a handle typed with spaces fails.","solutions":["Ensure the username segment matches /^[a-zA-Z0-9._]+$/ with length 1-30, no leading/trailing '.', no consecutive '..'.","Confirm the URL uses threads.net or threads.com as the domain; other domains produce invalidUrl with the same message.","If the input is a bare handle, prefix understanding: handles without '@' are accepted because the second pathType has urlPrefix '' but storagePrefix '@'.","Strip query strings and fragments from pasted URLs before validating, since the ?# guard rejects them as invalidUsername."],"exampleFix":"// before (fails — leading period)\nvalidateThreadsUrl('https://www.threads.net/@.username')\n// after\nvalidateThreadsUrl('https://www.threads.net/@username')","handlingStrategy":"validation","validationCode":"function isValidThreadsHandle(handle: string): boolean {\n  // INSTAGRAM_USERNAME_RULE: ASCII letters/numbers/._ , 1-30, no leading/trailing/consecutive '.'\n  return /^[a-zA-Z0-9._]+$/.test(handle)\n    && handle.length >= 1 && handle.length <= 30\n    && !handle.startsWith('.') && !handle.endsWith('.')\n    && !handle.includes('..');\n}","typeGuard":"function isThreadsHandle(value: unknown): value is string {\n  return typeof value === 'string'\n    && /^[a-zA-Z0-9._]+$/.test(value)\n    && value.length >= 1 && value.length <= 30\n    && !value.startsWith('.') && !value.endsWith('.')\n    && !value.includes('..');\n}","tryCatchPattern":"try {\n  const url = validateThreadsUrl(input);\n} catch (e) {\n  if (e instanceof Error && /threads\\.net/.test(e.message)) {\n    // surface to the form field\n  }\n  throw e;\n}","preventionTips":["Validate against the Instagram username rule before calling validateThreadsUrl.","Strip leading '@' from typed handles before validation if you handle bare input manually.","Ensure the URL domain is threads.net or threads.com; other domains yield the same error message."],"tags":["validation","social-urls","threads","input-validation"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}