TryGhost/Ghost · warning

Your Username is not a valid Mastodon Username

Error message

Your Username is not a valid Mastodon Username

What it means

Thrown by mastodonHandleToUrl when converting a stored handle to a canonical URL: the handle is empty, or doesn't match either Mastodon handle pattern (@username@instance or instance/@username), or the instance portion fails validator.isFQDN. This is the handle→URL direction, used when rendering a stored Mastodon profile link.

Source

Thrown at apps/admin/src/settings/app/utils/social-urls/mastodon.ts:49

        // If there's a second @, validate that part too
        if (rest.includes('@')) {
            const [, userInstance] = rest.split('@');
            if (!validator.isFQDN(userInstance)) {
                throw new Error(errMessage);
            }
        }

        return `https://${normalizedUrl}`;
    }

    throw new Error(errMessage);
}

// Converts a Mastodon handle to URL
export const mastodonHandleToUrl = (handle: string) => {
    const errMessage = 'Your Username is not a valid Mastodon Username';
    if (!handle) {
        throw new Error(errMessage);
    }

    // Check if it's in @username@instance format
    if (handle.match(/^@[^@]+@[^/]+$/)) {
        const [username, instance] = handle.split('@').slice(1);
        if (!validator.isFQDN(instance)) {
            throw new Error(errMessage);
        }
        return `https://${instance}/@${username}`;
    }

    // Check if it's in instance/@username format
    if (handle.match(/^[^/]+\.[^/]+\/@[^/]+(@[^/]+)?$/)) {
        const [instance, rest] = handle.split('/@');
        if (!validator.isFQDN(instance)) {
            throw new Error(errMessage);
        }

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Guard against empty handles at the call site before invoking mastodonHandleToUrl.
  2. Validate/normalise handles at write time using validateMastodonUrl so only well-formed handles are ever stored.
  3. If rendering from untrusted stored data, wrap in try-catch and fall back to omitting the link rather than crashing the UI.
  4. Audit stored Mastodon handles for ones that no longer pass validation after a regex/validator version change.

Example fix

// before — throws (and can crash a render) on empty/corrupt stored handle
const url = mastodonHandleToUrl(profile.mastodon);

// after — guard empty, try-catch the rest
const handle = profile.mastodon?.trim();
if (!handle) {
    return null;
}
try {
    return mastodonHandleToUrl(handle);
} catch {
    return null; // skip rendering the link for invalid stored handles
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard empty handles before calling the converter
function safeMastodonHandleToUrl(handle: string | undefined | null): string | null {
    const h = handle?.trim();
    if (!h) return null;
    // quick shape check before invoking the throwing converter
    if (!/^@[^@]+@[^/]+$/.test(h) && !/^[^/]+\.[^/]+\/@[^/]+(@[^/]+)?$/.test(h)) return null;
    return null; // caller should still try-catch for FQDN failures
}

Type guard

null

Try / catch

const handle = profile.mastodon?.trim();
if (!handle) return null;
try {
    return mastodonHandleToUrl(handle);
} catch {
    return null; // skip rendering the link for invalid stored handles
}

Prevention

When it happens

Trigger: mastodonHandleToUrl(handle) where handle is '' (empty → immediate throw), or handle matches ^@[^@]+@[^/]+$ / ^[^/]+\.[^/]+\/@[^/]+(@[^/]+)?$ but the instance isn't an FQDN, or handle matches neither pattern at all (e.g. 'someuser', 'user@mastodon').

Common situations: Stored handle is empty due to data corruption or incomplete migration; handle was persisted before stricter validation and doesn't conform; UI renders a profile link from a handle that was never validated; concurrent deletion left an empty string.

Related errors


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