TryGhost/Ghost · warning
Your Username is not a valid Bluesky Username
Error message
Your Username is not a valid Bluesky Username
What it means
Thrown by checkUsername inside createPlatformValidator when the Bluesky username extracted from a URL or handle fails to match any of the three accepted identifier shapes: a DID (did:plc: + 24 Base32 chars), a short handle (1-15 chars of [a-zA-Z0-9._]), or a domain handle (dot-separated, up to 191 chars). The message is the platform definition's errors.invalidUsername string. It signals that the input parsed as a Bluesky profile URL but the identifier portion is malformed.
Source
Thrown at apps/admin/src/settings/app/utils/social-urls/bluesky.ts:27
// domain handle: requires a dot, max 191 chars
// (the lookahead does the length check because + is unbounded)
/^(?=.{1,191}$)[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/
];
const bluesky = createPlatformValidator({
domains: ['bsky.app'],
www: false,
pathTypes: [
// bsky.app/profile/@username is a common paste (users type Bluesky
// handles with a leading @ out of habit); the @ is decorative here,
// not a marker for a competing path type, so it's still stripped
{urlPrefix: 'profile/', storagePrefix: '', tolerateLeadingAt: true, rule: {patterns: BLUESKY_USERNAME_PATTERNS}}
],
// DIDs are case-insensitive identifiers, canonically lowercase
transformUsername: username => (/^did:plc:/i.test(username) ? username.toLowerCase() : username),
errors: {
invalidUrl: 'The URL must be in a format like https://bsky.app/profile/yourUsername',
invalidUsername: 'Your Username is not a valid Bluesky Username'
}
});
export const validateBlueskyUrl = bluesky.validate;
export const blueskyHandleToUrl = bluesky.handleToUrl;
export const blueskyUrlToHandle = bluesky.urlToHandle;
View on GitHub (pinned to 47d8b0e2ad)
Solutions
- Inspect the extracted username: it must match /^did:plc:[a-z2-7]{24}$/, /^[a-zA-Z0-9._]{1,15}$/, or /^(?=.{1,191}$)[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/.
- If the input is a URL, ensure only the single path segment after profile/ is the identifier — remove trailing /post/... or /lists/... paths before validating, or rely on the pipeline which takes only the first segment.
- If storing DIDs, lowercase them; the transformUsername only lowercases did:plc: prefixed values so a mixed-case short handle is accepted but a DID with uppercase after the prefix must already be lowercase or it will be lowercased automatically.
- Strip any leading '@' before calling handleToUrl if you bypass validate, though tolerateLeadingAt already handles bsky.app/profile/@user pastes.
Example fix
// before (fails — space in handle)
validateBlueskyUrl('https://bsky.app/profile/my handle')
// after
validateBlueskyUrl('https://bsky.app/profile/myhandle') Defensive patterns
Strategy: validation
Validate before calling
const BLUESKY_PATTERNS = [
/^did:plc:[a-z2-7]{24}$/,
/^[a-zA-Z0-9._]{1,15}$/,
/^(?=.{1,191}$)[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/
];
function isValidBlueskyHandle(handle: string): boolean {
return BLUESKY_PATTERNS.some(p => p.test(handle));
}
// run before validateBlueskyUrl on the extracted segment Type guard
function isBlueskyHandle(value: unknown): value is string {
return typeof value === 'string' && [
/^did:plc:[a-z2-7]{24}$/,
/^[a-zA-Z0-9._]{1,15}$/,
/^(?=.{1,191}$)[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$/
].some(p => p.test(value));
} Try / catch
try {
const url = validateBlueskyUrl(input);
} catch (e) {
if (e instanceof Error && /not a valid Bluesky Username/.test(e.message)) {
// show field-level validation error
}
throw e;
} Prevention
- Validate the handle format before calling validateBlueskyUrl using the same three patterns.
- Pre-strip query strings and fragments from pasted URLs before validation.
- Use blueskyUrlToHandle (which returns null instead of throwing) when you only need to extract, not canonicalize.
When it happens
Trigger: Calling validateBlueskyUrl, blueskyHandleToUrl, or blueskyUrlToHandle with input whose path segment after 'profile/' is not a valid DID, short handle, or domain handle. Examples: 'https://bsky.app/profile/user name' (space), 'https://bsky.app/profile/-bad' (leading dash in a non-domain handle), 'https://bsky.app/profile/' (empty, caught earlier as invalidUrl), 'https://bsky.app/profile/a.b.c.d.e.f.g' where the segment exceeds 15 chars and has no dot so it matches no pattern, or a handle like 'did:plc:XYZ' where XYZ contains invalid Base32 characters.
Common situations: User pastes a Bluesky URL with a trailing query string or fragment that leaks into the username (caught by the ?# check first), copies a profile URL for a suspended/renamed account, or types a handle with characters Bluesky forbids. Developers migrating from a system that stored handles with a leading '@' will usually pass because tolerateLeadingAt strips it, but handles stored with other decorations (slashes, spaces) will fail.
Related errors
- The URL must be in a format like https://bsky.app/profile/yo
- The URL must be in a format like https://www.threads.net/@yo
- The URL must be in a format like @username@instance.tld or h
- Your Username is not a valid Mastodon Username
- The URL must be in a format like https://x.com/yourUsername
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/c0629af7dbe7e80b.
Report an issue: GitHub.