perspective-dev/perspective · error · TypeError
TileSourceSpec.template uses
Error message
TileSourceSpec.template uses "{s}" but no subdomains were given What it means
Cross-field consistency check in parseTileSourceSpec: a template containing the {s} placeholder needs at least one entry in `subdomains` to substitute for it. The spec references subdomain cycling but supplies none, so at runtime the URL would contain a literal {s}; the validator rejects it before any tile is requested.
Solutions
- Provide subdomains: ["a","b","c"] alongside the {s} template
- Remove {s} from the template URL if the provider serves from a single host
- Use a full explicit template like https://a.tile.example.com/{z}/{x}/{y}.png instead of {s}
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at packages/viewer-charts/src/ts/map/tile-source.ts:170 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/b83ddb4bcdc4fe27.
Report an issue: GitHub.
Appendix: source
Thrown at packages/viewer-charts/src/ts/map/tile-source.ts:170
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(
'TileSourceSpec.template uses "{s}" but no subdomains were given',
);
}
const num = (key: string, dflt: number): number => {
const v = raw[key];
if (v === undefined) {
return dflt;
}
if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
throw new TypeError(
`TileSourceSpec.${key} must be a positive number`,
);
}
return v;
};View on GitHub (pinned to 11c8238c0c)