TryGhost/Ghost · warning
The URL must be in a format like @username@instance.tld or h
Error message
The URL must be in a format like @username@instance.tld or https://instance.tld/@username or https://website.com/@username@instance.tld
What it means
Thrown by validateMastodonUrl when input can't be recognised as either the @username@instance handle form or the instance.tld/@username URL form, OR when it matches one of those forms but the instance portion fails validator.isFQDN. Mastodon is federated so there's no fixed domain — validation hinges on the syntactic shape plus a valid fully-qualified domain for the instance.
Source
Thrown at apps/admin/src/settings/app/utils/social-urls/mastodon.ts:19
import validator from 'validator';
// Validates and normalizes Mastodon URLs
export function validateMastodonUrl(newUrl: string) {
const errMessage = 'The URL must be in a format like @username@instance.tld or https://instance.tld/@username or https://website.com/@username@instance.tld';
if (!newUrl) {
return '';
}
let normalizedUrl = newUrl;
// Remove https:// or http:// if present
normalizedUrl = normalizedUrl.replace(/^https?:\/\//, '');
// Check if it's in @username@instance format
if (normalizedUrl.match(/^@[^@]+@[^/]+$/)) {
const [username, instance] = normalizedUrl.split('@').slice(1);
if (!validator.isFQDN(instance)) {
throw new Error(errMessage);
}
return `https://${instance}/@${username}`;
}
// Check if it's in instance/@username format
if (normalizedUrl.match(/^[^/]+\.[^/]+\/@[^/]+(@[^/]+)?$/)) {
const [instance, rest] = normalizedUrl.split('/@');
if (!validator.isFQDN(instance)) {
throw new Error(errMessage);
}
// If there's a second @, validate that part too
if (rest.includes('@')) {
const [, userInstance] = rest.split('@');
if (!validator.isFQDN(userInstance)) {
throw new Error(errMessage);
}
}View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Match one of the documented formats: '@username@instance.tld', 'https://instance.tld/@username', or 'https://website.com/@username@instance.tld'.
- Ensure the instance portion is a valid fully-qualified domain (validator.isFQDN): real TLD, no underscores, public DNS resolvable shape.
- Strip trailing slashes, query strings, and fragments before validating — the regexes are strict about the whole string matching.
- If building integrations, prefer the non-throwing mastodonUrlToHandle to test a URL first (returns null instead of throwing).
Example fix
// before — throws on malformed federated handle
const normalized = validateMastodonUrl(input);
// after — non-throwing pre-check, then validate
import {mastodonUrlToHandle} from './mastodon';
const trimmed = input.trim();
if (!trimmed) {
setNormalized('');
} else if (mastodonUrlToHandle(trimmed) === null && !/^@[^@]+@[^/]+$/.test(trimmed.replace(/^https?:\/\//, ''))) {
setFieldError('mastodon', 'Use @username@instance.tld or https://instance.tld/@username');
} else {
try {
setNormalized(validateMastodonUrl(trimmed));
} catch (e) {
setFieldError('mastodon', e instanceof Error ? e.message : 'Invalid Mastodon URL');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Non-throwing pre-check using the URL→handle extractor
import {mastodonUrlToHandle} from './mastodon';
function looksLikeMastodon(input: string): boolean {
if (!input) return true; // empty is allowed (returns '')
const stripped = input.trim().replace(/^https?:\/\//, '');
if (/^@[^@]+@[^/]+$/.test(stripped)) return true; // @user@instance handle form
return mastodonUrlToHandle(input) !== null; // instance/@user URL form
} Type guard
null
Try / catch
try {
const normalized = validateMastodonUrl(input.trim());
} catch (e) {
setFieldError('mastodon', e instanceof Error ? e.message : 'Invalid Mastodon URL');
} Prevention
- Match the documented formats exactly: @username@instance.tld, https://instance.tld/@username, https://website.com/@username@instance.tld.
- Ensure the instance portion is a valid FQDN (validator.isFQDN).
- Strip trailing slashes and query strings before validating — the regexes match the whole string.
- Use mastodonUrlToHandle for non-throwing URL checks.
When it happens
Trigger: Input that after stripping the protocol: (a) doesn't match ^@[^@]+@[^/]+$ nor ^[^/]+\.[^/]+\/@[^/]+(@[^/]+)?$ — e.g. 'mastodon.social', '@user', 'example.com/user' (missing @); (b) matches @username@instance but the instance isn't an FQDN (e.g. '@user@not_a_domain', '@user@localhost'); (c) matches instance/@username but the instance portion fails FQDN; (d) the second @ in a website.com/@username@instance form has an invalid instance.
Common situations: User pastes a bare username without the @; user enters only the instance; instance uses an IP/localhost/invalid TLD; user typed a Profile URL from a non-Mastodon service; copy-paste included trailing slashes or query strings that break the strict regex.
Related errors
- Your Username is not a valid Mastodon Username
- The URL must be in a format like https://x.com/yourUsername
- Your Username is not a valid Twitter Username
- The URL must be in a format like https://www.linkedin.com/in
- Your Username is not a valid LinkedIn Username
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/8ef8ea266db0ea57.
Report an issue: GitHub.