perspective-dev/perspective · error · TypeError

TileSourceSpec.template must contain

Error message

TileSourceSpec.template must contain "${placeholder}"

What it means

After validating the template string, parseTileSourceSpec requires the {z}, {x}, {y} placeholders so the tile URL can be filled with zoom/x/y coordinates per tile request. A template missing any of these (e.g. a plain WMS URL or a typo like {Z}) cannot be used by the tile layer.

Solutions

  1. Include all three placeholders, e.g. https://tile.example.com/{z}/{x}/{y}.png
  2. Fix casing/typos in placeholders ({Z}/{X}/{Y} are not matched)
  3. Use a template from a standard raster XYZ tile provider as reference
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at packages/viewer-charts/src/ts/map/tile-source.ts:152 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/75ad0d298b24e91c. Report an issue: GitHub.

Appendix: source

Thrown at packages/viewer-charts/src/ts/map/tile-source.ts:152

    const raw = input as Record<string, unknown>;
    const str = (key: string): string => {
        const v = raw[key];
        if (typeof v !== "string" || v.length === 0) {
            throw new TypeError(
                `TileSourceSpec.${key} must be a non-empty string`,
            );
        }

        return v;
    };

    const id = str("id");
    const label = str("label");
    const template = str("template");
    const attribution = str("attribution");
    for (const placeholder of ["{z}", "{x}", "{y}"]) {
        if (!template.includes(placeholder)) {
            throw new TypeError(
                `TileSourceSpec.template must contain "${placeholder}"`,
            );
        }
    }

    const rawSubdomains = raw["subdomains"] ?? [];
    if (
        !Array.isArray(rawSubdomains) ||
        rawSubdomains.some((s) => typeof s !== "string" || s.length === 0)
    ) {
        throw new TypeError(
            "TileSourceSpec.subdomains must be an array of non-empty strings",
        );
    }

    const subdomains = Object.freeze([...rawSubdomains] as string[]);
    if (template.includes("{s}") && subdomains.length === 0) {
        throw new TypeError(

View on GitHub (pinned to 11c8238c0c)