TryGhost/Ghost · warning

The URL must be in a format like https://www.threads.net/@yo

Error message

The URL must be in a format like https://www.threads.net/@yourUsername

What it means

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.

Source

Thrown at apps/admin/src/settings/app/utils/social-urls/threads.ts:16

import {INSTAGRAM_USERNAME_RULE} from './instagram';
import {createPlatformValidator} from './platform-validator';

// Threads accounts are Instagram accounts, so the username rule is shared.
// threads.com URLs are accepted and canonicalised to www.threads.net.
const threads = createPlatformValidator({
    domains: ['threads.net', 'threads.com'],
    www: true,
    pathTypes: [
        // /@username is the canonical form, but /username also resolves (it
        // redirects on threads.net), so URLs without the @ are accepted too
        {urlPrefix: '@', storagePrefix: '@', rule: INSTAGRAM_USERNAME_RULE},
        {urlPrefix: '', storagePrefix: '@', rule: INSTAGRAM_USERNAME_RULE}
    ],
    errors: {
        invalidUrl: 'The URL must be in a format like https://www.threads.net/@yourUsername',
        invalidUsername: 'The URL must be in a format like https://www.threads.net/@yourUsername'
    }
});

export const validateThreadsUrl = threads.validate;
export const threadsHandleToUrl = threads.handleToUrl;
export const threadsUrlToHandle = threads.urlToHandle;

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Ensure the username segment matches /^[a-zA-Z0-9._]+$/ with length 1-30, no leading/trailing '.', no consecutive '..'.
  2. Confirm the URL uses threads.net or threads.com as the domain; other domains produce invalidUrl with the same message.
  3. If the input is a bare handle, prefix understanding: handles without '@' are accepted because the second pathType has urlPrefix '' but storagePrefix '@'.
  4. Strip query strings and fragments from pasted URLs before validating, since the ?# guard rejects them as invalidUsername.

Example fix

// before (fails — leading period)
validateThreadsUrl('https://www.threads.net/@.username')
// after
validateThreadsUrl('https://www.threads.net/@username')
Defensive patterns

Strategy: validation

Validate before calling

function isValidThreadsHandle(handle: string): boolean {
  // INSTAGRAM_USERNAME_RULE: ASCII letters/numbers/._ , 1-30, no leading/trailing/consecutive '.'
  return /^[a-zA-Z0-9._]+$/.test(handle)
    && handle.length >= 1 && handle.length <= 30
    && !handle.startsWith('.') && !handle.endsWith('.')
    && !handle.includes('..');
}

Type guard

function isThreadsHandle(value: unknown): value is string {
  return typeof value === 'string'
    && /^[a-zA-Z0-9._]+$/.test(value)
    && value.length >= 1 && value.length <= 30
    && !value.startsWith('.') && !value.endsWith('.')
    && !value.includes('..');
}

Try / catch

try {
  const url = validateThreadsUrl(input);
} catch (e) {
  if (e instanceof Error && /threads\.net/.test(e.message)) {
    // surface to the form field
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/113c24428137d9e4. Report an issue: GitHub.