badges/shields · warning · NotFound

spec not found or unreadable

Error message

spec not found or unreadable

What it means

The Swagger validator badge's `transform` inspects schemaValidationMessages from validator.swagger.io. When exactly one error-level message equals `Can't read from file <specUrl>`, it throws NotFound 'spec not found or unreadable' — the validator could not fetch the OpenAPI/Swagger document at the given URL.

Source

Thrown at services/swagger/swagger.service.js:74

    return this._requestJson({
      url: 'https://validator.swagger.io/validator/debug',
      schema,
      options: {
        searchParams: {
          url: specUrl,
        },
      },
    })
  }

  transform({ json, specUrl }) {
    const valMessages = json.schemaValidationMessages
    if (!valMessages || valMessages.length === 0) {
      return { status: 'valid' }
    } else if (valMessages.length === 1) {
      const { message, level } = valMessages[0]
      if (level === 'error' && message === `Can't read from file ${specUrl}`) {
        throw new NotFound({ prettyMessage: 'spec not found or unreadable' })
      }
    }
    if (valMessages.every(msg => msg.level === 'warning')) {
      return { status: 'valid' }
    }
    return { status: 'invalid' }
  }

  async handle(_routeParams, { specUrl }) {
    const json = await this.fetch({ specUrl })
    const { status } = this.transform({ json, specUrl })
    return this.constructor.render({ status })
  }
}

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Open the spec URL in an external browser/incognito and confirm it returns the document with HTTP 200
  2. Publish the spec at a publicly reachable URL; the validator cannot access private networks or authenticated endpoints
  3. Fix URL typos and ensure the content-type/extension looks like JSON or YAML the validator can read
  4. Whitelist validator.swagger.io / fix TLS so the spec is fetchable; only after that check whether remaining validation messages indicate real spec errors

Example fix

// before
/badge/swagger/v2/1.0/https://internal.example.com/local-api.json  (404 externally)
// after
/badge/swagger/v2/1.0/https://api.example.com/public-openapi.json
Defensive patterns

Strategy: validation

Validate before calling

const specUrl = 'https://api.example.com/openapi.json';
const res = await fetch(specUrl);
if (!res.ok) throw new Error(`spec unreachable: HTTP ${res.status}`); // validator cannot read it either

Type guard

const isPublicUrl = (u) => { try { const { hostname } = new URL(u); return !['localhost','127.0.0.1'].includes(hostname) && !/^(10|192\.168|172\.(1[6-9]|2\d|3[01]))\./.test(hostname); } catch { return false; } };

Try / catch

try {
  return await swaggerBadge({ specUrl });
} catch (err) {
  if (err prettyMessage === 'spec not found or unreadable') {
    // check spec URL reachability/auth/TLS before retrying
  } else throw err;
}

Prevention

When it happens

Trigger: Calling /swagger (e.g. /swagger/v2/1.0/https://host/spec.json) with a spec URL that returns 404, requires auth, blocks the validator's user agent, is on localhost/private network, or has invalid TLS.

Common situations: Spec hosted behind a login (intranet, private API); URL typo or file renamed; localhost/dev server URL used in a public badge; server rejecting external requests via firewall or bot protection (Cloudflare); self-signed certificate.

Related errors


AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30). Data as JSON: /api/errors/c2692221d32bfdbc. Report an issue: GitHub.