projectdiscovery/nuclei · error

URL does not appear to be an OpenAPI JSON spec

Error message

URL does not appear to be an OpenAPI JSON spec

What it means

OpenAPIDownloader.Download (pkg/input/formats/openapi/downloader.go) rejects any URL that does not end exactly with `.json` (case-sensitive strings.HasSuffix, before any HTTP request is made). The check exists because this downloader only understands OpenAPI 3.0 JSON documents, so a URL without the .json suffix is treated as not an OpenAPI JSON spec.

Source

Thrown at pkg/input/formats/openapi/downloader.go:31

	"github.com/pkg/errors"
	"github.com/projectdiscovery/nuclei/v3/pkg/input/formats"
	"github.com/projectdiscovery/retryablehttp-go"
)

// OpenAPIDownloader implements the SpecDownloader interface for OpenAPI 3.0 specs
type OpenAPIDownloader struct{}

// NewDownloader creates a new OpenAPI downloader
func NewDownloader() formats.SpecDownloader {
	return &OpenAPIDownloader{}
}

// This function downloads an OpenAPI 3.0 spec from the given URL and saves it to tmpDir
func (d *OpenAPIDownloader) Download(urlStr, tmpDir string, httpClient *retryablehttp.Client) (string, error) {
	// Validate URL format, OpenAPI 3.0 specs are typically JSON
	if !strings.HasSuffix(urlStr, ".json") {
		return "", fmt.Errorf("URL does not appear to be an OpenAPI JSON spec")
	}

	const maxSpecSizeBytes = 10 * 1024 * 1024 // 10MB

	// Use provided httpClient or create a fallback
	var client *http.Client
	if httpClient != nil {
		client = httpClient.HTTPClient
	} else {
		// Fallback to simple client if no httpClient provided
		client = &http.Client{Timeout: 30 * time.Second}
	}

	resp, err := client.Get(urlStr)
	if err != nil {
		return "", errors.Wrap(err, "failed to download OpenAPI spec")
	}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Point -u at the raw spec URL ending in .json (open the docs UI and copy the spec link behind it)
  2. If the spec is YAML or Swagger 2.x, use `-im swagger`, which accepts .json, .yaml and .yml
  3. Strip query strings and fragments from the URL
  4. If the server cannot serve a .json-suffixed URL, download the spec manually and pass the local file with `nuclei -l spec.json` (format is auto-detected)

Example fix

# before
nuclei -im openapi -u https://api.example.com/api-docs

# after
nuclei -im openapi -u https://api.example.com/openapi.json
Defensive patterns

Strategy: validation

Validate before calling

func isOpenAPIDownloadable(u string) bool {
    if !strings.HasPrefix(u, "http://") && !strings.HasPrefix(u, "https://") {
        return false
    }
    return strings.HasSuffix(u, ".json")
}

Try / catch

if _, err := downloader.Download(u, tmp, client); err != nil {
    if strings.Contains(err.Error(), "does not appear to be an OpenAPI JSON spec") {
        // fall back: download manually or switch to swagger mode for .yaml/.yml
    }
}

Prevention

When it happens

Trigger: `nuclei -im openapi -u https://host/api-docs` (no extension); a YAML spec URL like https://host/openapi.yaml; uppercase .JSON; URLs with query strings or fragments appended after the filename (openapi.json?v=2); pasting a Swagger UI page URL instead of the raw spec link.

Common situations: Copying the interactive docs URL (e.g. /swagger-ui or /api-docs) rather than the raw spec; specs served by gateways without file extensions; URLs carrying auth query tokens; Windows-style casing .JSON.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/1398c787efe7de80. Report an issue: GitHub.