TryGhost/Ghost · warning

A view with this name already exists

Error message

A view with this name already exists

What it means

Thrown by buildViewsForSave (member-views.ts) when saving a member view whose name — case-insensitively trimmed and compared — collides with another saved view on the same 'members' route. There are two throw sites: line 91 (renaming an existing view to a name another view already has, with the target index excluded) and line 100 (creating a new view whose name duplicates an existing one). The check is normalizeSharedViewName (trim + lower-case).

Source

Thrown at apps/admin/src/members/member-views.ts:91

}

export function buildViewsForSave(allViews: SharedView[], name: string, filter: string, originalView?: MemberView): SharedView[] {
    const nextView = buildMemberView(name, filter);

    if (originalView) {
        const matchingIndexes = findMatchingSharedViewIndexes(allViews, originalView);

        if (matchingIndexes.length === 0) {
            throw new Error(VIEW_UPDATE_NOT_FOUND_ERROR);
        }

        if (matchingIndexes.length > 1) {
            throw new Error(VIEW_UPDATE_AMBIGUOUS_ERROR);
        }

        const targetIndex = matchingIndexes[0];
        if (hasSharedViewNameConflict(allViews, nextView, targetIndex)) {
            throw new Error(VIEW_EXISTS_ERROR);
        }

        return allViews.map((view, index) => {
            return index === targetIndex ? nextView : view;
        });
    }

    if (hasSharedViewNameConflict(allViews, nextView)) {
        throw new Error(VIEW_EXISTS_ERROR);
    }

    return [...allViews, nextView];
}

export function buildViewsForDelete(allViews: SharedView[], view: MemberView): SharedView[] {
    const matchingIndexes = findMatchingSharedViewIndexes(allViews, view);

    if (matchingIndexes.length === 0) {

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Pre-check for conflict before calling buildViewsForSave using hasSharedViewNameConflict, and prompt the user to pick a distinct name.
  2. If the duplicate is unintended, audit the stored shared_views setting for near-duplicate names (same after trim + lower-case) and deduplicate.
  3. For concurrent-edit races, re-read the latest shared_views from the API immediately before saving and retry.
  4. Strip leading/trailing whitespace and normalize case in the UI before submitting so the user sees the canonical form.

Example fix

// before
const next = buildViewsForSave(allViews, name, filter, originalView); // throws on duplicate name

// after — detect the conflict first and surface a user-facing message
import {hasSharedViewNameConflict} from './shared-views';
const nextView = {name: name.trim(), route: 'members' as const, filter: {filter}};
if (hasSharedViewNameConflict(allViews, nextView, originalViewIndex)) {
    setFieldError('name', 'A view with this name already exists');
    return;
}
const next = buildViewsForSave(allViews, name, filter, originalView);
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check for a name conflict before saving
import {hasSharedViewNameConflict} from './shared-views';
import type {SharedView} from './shared-views';

function wouldConflict(allViews: SharedView[], name: string, excludeIndex?: number): boolean {
    return hasSharedViewNameConflict(allViews, {name, route: 'members'}, excludeIndex);
}

Type guard

// (No custom error class; detect by message.)
function isViewExistsError(e: unknown): boolean {
    return e instanceof Error && e.message === 'A view with this name already exists';
}

Try / catch

try {
    const next = buildViewsForSave(allViews, name, filter, originalView);
} catch (e) {
    if (e instanceof Error && e.message === 'A view with this name already exists') {
        setFieldError('name', e.message);
        return;
    }
    throw e;
}

Prevention

When it happens

Trigger: On create: hasSharedViewNameConflict(allViews, nextView) returns true — another view on route 'members' has the same trimmed/lowercased name. On update: hasSharedViewNameConflict(allViews, nextView, targetIndex) returns true — a view OTHER than the one being updated shares the normalized name. Examples: saving 'My View' when 'my view' exists; editing a view and renaming it to 'Drafts' while another 'drafts' view exists.

Common situations: User types a name that differs only by case or whitespace from an existing saved view; shared_views setting was imported/migrated containing near-duplicate names; two browser tabs editing views concurrently so the second save collides; copy-paste of a view name.

Related errors


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