{"record":{"id":"d1959ba189e28aef","repo":"projectdiscovery/nuclei","slug":"not-an-openapi-spec-missing-openapi-field","errorCode":null,"errorMessage":"not an OpenAPI spec (missing 'openapi' field)","messagePattern":"not an OpenAPI spec \\(missing 'openapi' field\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/input/formats/openapi/downloader.go","lineNumber":77,"sourceCode":"\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to read response body\")\n\t}\n\n\t// Validate it's a valid JSON and has OpenAPI structure\n\tvar spec map[string]interface{}\n\tif err := json.Unmarshal(bodyBytes, &spec); err != nil {\n\t\treturn \"\", fmt.Errorf(\"downloaded content is not valid JSON: %w\", err)\n\t}\n\n\t// Check if it's an OpenAPI 3.0 spec\n\tif openapi, exists := spec[\"openapi\"]; exists {\n\t\tif openapiStr, ok := openapi.(string); ok && strings.HasPrefix(openapiStr, \"3.\") {\n\t\t\t// Valid OpenAPI 3.0 spec\n\t\t} else {\n\t\t\treturn \"\", fmt.Errorf(\"not a valid OpenAPI 3.0 spec (found version: %v)\", openapi)\n\t\t}\n\t} else {\n\t\treturn \"\", fmt.Errorf(\"not an OpenAPI spec (missing 'openapi' field)\")\n\t}\n\n\t// Extract host from URL for server configuration\n\tparsedURL, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn \"\", errors.Wrap(err, \"failed to parse URL\")\n\t}\n\thost := parsedURL.Host\n\tscheme := parsedURL.Scheme\n\tif scheme == \"\" {\n\t\tscheme = \"https\"\n\t}\n\n\t// Add servers section if missing or empty\n\tservers, exists := spec[\"servers\"]\n\tif !exists || servers == nil {\n\t\tspec[\"servers\"] = []map[string]interface{}{{\"url\": scheme + \"://\" + host}}\n\t} else if serverList, ok := servers.([]interface{}); ok && len(serverList) == 0 {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/input/formats/openapi/downloader.go#L59-L95","documentation":"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.","triggerScenarios":"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\": ...}.","commonSituations":"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.","solutions":["If the document has `swagger: 2.x`, use `-im swagger`","Open the URL and confirm the top-level keys: an OpenAPI 3 spec must start with \"openapi\": \"3...\"","Locate the real spec URL from the docs UI (usually linked behind a 'download/open specification' action)","If the JSON is an envelope, extract the inner spec object, save it locally, and run `nuclei -l spec.json`"],"exampleFix":"# before (document is Swagger 2.0 -> missing 'openapi' field)\nnuclei -im openapi -u https://host/spec.json\n\n# after\nnuclei -im swagger -u https://host/spec.json","handlingStrategy":"validation","validationCode":"var spec map[string]any\nif err := json.Unmarshal(body, &spec); err != nil {\n    return err\n}\nif _, ok := spec[\"openapi\"]; !ok {\n    if _, isSwagger := spec[\"swagger\"]; isSwagger {\n        return fmt.Errorf(\"document is Swagger 2.x; use -im swagger\")\n    }\n    return fmt.Errorf(\"document is not an API spec\")\n}","typeGuard":"func isAPIspec(spec map[string]any) (mode string, ok bool) {\n    if v, _ := spec[\"openapi\"].(string); strings.HasPrefix(v, \"3.\") {\n        return \"openapi\", true\n    }\n    if v, _ := spec[\"swagger\"].(string); strings.HasPrefix(v, \"2.\") {\n        return \"swagger\", true\n    }\n    return \"\", false\n}","tryCatchPattern":"if strings.Contains(err.Error(), \"missing 'openapi' field\") {\n    // inspect the document keys: switch to swagger mode or find the real spec URL\n}","preventionTips":["Sniff the document's top-level key (openapi vs swagger) once and cache the right input mode","Do not reuse docs-UI URLs as spec URLs","Store known-good spec URLs per service in config"],"tags":["openapi","spec-validation","input-mode","download"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}