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

  1. Use the concrete service class (e.g. JsonPath / the registered service) rather than the base class
  2. Add a fetch() override in your subclass that performs the request (see services/dynamic-common.js default)
  3. 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

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.