badges/shields · error · Error
fetch() function not implemented for ${this.constructor.name
Error message
fetch() function not implemented for ${this.constructor.name} What it means
This is a guard method on a BaseJsonPathService-style class: the abstract fetch() must be overridden by a concrete service that actually performs the HTTP request and parses the JSON. Calling fetch() directly on the base class (or an instance that forgot to implement it) throws this Error, naming the class that lacks the implementation.
Source
Thrown at services/dynamic/json-path.js:34
export default superclass =>
class extends superclass {
static category = 'dynamic'
static defaultBadgeData = { label: 'custom badge' }
/**
* Request data from an upstream API, transform it to JSON and validate against a schema
*
* @param {object} attrs Refer to individual attrs
* @param {Joi} attrs.schema Joi schema to validate the response transformed to JSON
* @param {string} attrs.url URL to request
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
* and custom error messages e.g: `{ 404: 'package not found' }`.
* This can be used to extend or override the
* [default](https://github.com/badges/shields/blob/master/services/dynamic-common.js#L8)
* @returns {object} Parsed response
*/
async fetch({ schema, url, httpErrors }) {
throw new Error(
`fetch() function not implemented for ${this.constructor.name}`,
)
}
async handle(namedParams, { url, query: pathExpression, prefix, suffix }) {
const data = await this.fetch({
schema: Joi.any(),
url,
httpErrors,
})
let values
try {
values = jp({ json: data, path: pathExpression, eval: false })
} catch (e) {
const { message } = e
if (
message.includes('prevented in JSONPath expression') ||View on GitHub (pinned to 766fd8bc89)
Solutions
- Use the concrete service class (e.g. JsonPath / the registered service) rather than the base class
- Add a fetch() override in your subclass that performs the request (see services/dynamic-common.js default)
- Restore the fetch() override if a refactor accidentally removed it
Example fix
// before
class MyService extends BaseJsonPathService {}
new MyService().fetch({ schema, url, httpErrors }) // throws
// after
class MyService extends BaseJsonPathService {
async fetch({ schema, url, httpErrors }) {
return this._fetch({ schema, url, httpErrors })
}
} Defensive patterns
Strategy: type-guard
Validate before calling
function isFetchable(ServiceClass) {
return typeof ServiceClass === 'function' &&
ServiceClass.prototype.fetch !== BaseJsonPathService.prototype.fetch
} Type guard
function canFetch(instance) {
return instance.fetch !== BaseJsonPathService.prototype.fetch
}
// instantiate only subclasses where canFetch(new Cls()) is true Try / catch
try {
const data = await service.fetch({ schema, url, httpErrors })
} catch (e) {
if (e.message.startsWith('fetch() function not implemented for')) {
throw new Error(`Use a concrete subclass, not ${service.constructor.name}`)
}
throw e
} Prevention
- Never instantiate abstract base service classes directly; use the registered concrete service
- When subclassing, always override fetch() (reuse services/dynamic-common.js default if suitable)
- Add a smoke test that calls handle() on every concrete service to catch missing overrides
When it happens
Trigger: Instantiating the base service class directly (e.g. in tests or custom code) and calling fetch() or handle() on it, instead of instantiating a subclass such as JsonPath that overrides fetch().
Common situations: Unit tests targeting the abstract base class; users wiring the service class into custom badge code and picking the wrong class; refactoring renamed the overriding fetch() so it no longer satisfies the contract.
Related errors
AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30).
Data as JSON: /api/errors/09b765238e940451.
Report an issue: GitHub.