projectdiscovery/nuclei · error

not an OpenAPI spec (missing 'openapi' field)

Error message

not an OpenAPI spec (missing 'openapi' field)

What it means

The downloaded URL returned valid JSON but the document has no top-level `openapi` key, which is the mandatory version marker of an OpenAPI 3.x spec. The downloader therefore cannot classify it. Typical bodies: a Swagger 2.0 doc (which uses `swagger:`), an unrelated JSON config/document, or a JSON wrapper served by a docs viewer.

Source

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

	if err != nil {
		return "", errors.Wrap(err, "failed to read response body")
	}

	// Validate it's a valid JSON and has OpenAPI structure
	var spec map[string]interface{}
	if err := json.Unmarshal(bodyBytes, &spec); err != nil {
		return "", fmt.Errorf("downloaded content is not valid JSON: %w", err)
	}

	// Check if it's an OpenAPI 3.0 spec
	if openapi, exists := spec["openapi"]; exists {
		if openapiStr, ok := openapi.(string); ok && strings.HasPrefix(openapiStr, "3.") {
			// Valid OpenAPI 3.0 spec
		} else {
			return "", fmt.Errorf("not a valid OpenAPI 3.0 spec (found version: %v)", openapi)
		}
	} else {
		return "", fmt.Errorf("not an OpenAPI spec (missing 'openapi' field)")
	}

	// Extract host from URL for server configuration
	parsedURL, err := url.Parse(urlStr)
	if err != nil {
		return "", errors.Wrap(err, "failed to parse URL")
	}
	host := parsedURL.Host
	scheme := parsedURL.Scheme
	if scheme == "" {
		scheme = "https"
	}

	// Add servers section if missing or empty
	servers, exists := spec["servers"]
	if !exists || servers == nil {
		spec["servers"] = []map[string]interface{}{{"url": scheme + "://" + host}}
	} else if serverList, ok := servers.([]interface{}); ok && len(serverList) == 0 {

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. If the document has `swagger: 2.x`, use `-im swagger`
  2. Open the URL and confirm the top-level keys: an OpenAPI 3 spec must start with "openapi": "3..."
  3. Locate the real spec URL from the docs UI (usually linked behind a 'download/open specification' action)
  4. If the JSON is an envelope, extract the inner spec object, save it locally, and run `nuclei -l spec.json`

Example fix

# before (document is Swagger 2.0 -> missing 'openapi' field)
nuclei -im openapi -u https://host/spec.json

# after
nuclei -im swagger -u https://host/spec.json
Defensive patterns

Strategy: validation

Validate before calling

var spec map[string]any
if err := json.Unmarshal(body, &spec); err != nil {
    return err
}
if _, ok := spec["openapi"]; !ok {
    if _, isSwagger := spec["swagger"]; isSwagger {
        return fmt.Errorf("document is Swagger 2.x; use -im swagger")
    }
    return fmt.Errorf("document is not an API spec")
}

Type guard

func isAPIspec(spec map[string]any) (mode string, ok bool) {
    if v, _ := spec["openapi"].(string); strings.HasPrefix(v, "3.") {
        return "openapi", true
    }
    if v, _ := spec["swagger"].(string); strings.HasPrefix(v, "2.") {
        return "swagger", true
    }
    return "", false
}

Try / catch

if strings.Contains(err.Error(), "missing 'openapi' field") {
    // inspect the document keys: switch to swagger mode or find the real spec URL
}

Prevention

When it happens

Trigger: Passing a Swagger 2.0 spec to `-im openapi`; pointing at a JSON API endpoint or docs metadata file instead of the spec; specs uploaded to generic file hosts that wrap content in an envelope like {"file": ...}.

Common situations: Not knowing whether an API documents with 2.0 or 3.x and guessing the mode; specs behind discovery endpoints like /api-docs that return session JSON; copy-pasting a JSON blob from a browser pretty-print view.

Related errors


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