Skip to content

@Tags with empty values generates empty-string tags in Swagger output #2216

Description

@AleksaMCode

When using @tags with empty values (for example @tags, @tags ,, or mixed @tags tag1, ,tag2), swag generates empty-string entries in the operation tags array (e.g. [""], ["", ""], ["tag1", "", "tag2"]) instead of filtering empties or treating it as untagged.

To Reproduce
Steps to reproduce the behavior:

  1. Create a Go API project using swag init and gin-swagger.
  2. Add endpoints with these annotations:
  1. Run swag init to generate docs.
  2. Open generated swagger.json (or /swagger/doc.json) and inspect operation tags.

Example I've used to generated the screenshot output below:

func main() {
	r := gin.Default()

	r.GET("/tournaments/:id", getTournamentByID)
	r.GET("/health", getHealth)
	r.GET("/about", getAbout)
	r.GET("/tags-comma-only", getTagsCommaOnly)
	r.GET("/tags-mixed", getTagsMixed)

	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))

	_ = r.Run(":8082")
}

// getTournamentByID godoc
// @Summary Get tournament by ID
// @Description Fake endpoint for local Swagger tag tests
// @Tags
// @Produce json
// @Param id path string true "Tournament ID"
// @Success 200 {object} map[string]any
// @Router /tournaments/{id} [get]
func getTournamentByID(c *gin.Context) {
	id := c.Param("id")

	c.JSON(http.StatusOK, gin.H{
		"id":     id,
		"name":   "Mock Tournament",
		"status": "scheduled",
	})
}

// getHealth godoc
// @Summary Health check
// @Description Reference endpoint with a normal tag
// @Tags reference
// @Produce json
// @Success 200 {object} map[string]any
// @Router /health [get]
func getHealth(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
	})
}

// getAbout godoc
// @Summary About endpoint
// @Description Endpoint without explicit tags annotation
// @Produce json
// @Success 200 {object} map[string]any
// @Router /about [get]
func getAbout(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"service": "gin-empty-tags",
		"mode":    "demo",
	})
}

// getTagsCommaOnly godoc
// @Summary Tags comma-only endpoint
// @Description Endpoint using comma-only tags annotation
// @Tags ,
// @Produce json
// @Success 200 {object} map[string]any
// @Router /tags-comma-only [get]
func getTagsCommaOnly(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"case": "comma-only-tags",
	})
}

// getTagsMixed godoc
// @Summary Tags mixed endpoint
// @Description Endpoint using mixed empty and non-empty tags annotation
// @Tags tag1, ,tag2
// @Produce json
// @Success 200 {object} map[string]any
// @Router /tags-mixed [get]
func getTagsMixed(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"case": "mixed-tags",
	})
}

Expected behavior
Empty tag tokens should be ignored.
Examples:

  • @tags -> no tags (untagged operation)
  • @tags , -> no tags
  • @tags tag1, ,tag2 -> ["tag1", "tag2"]

Screenshots

Image

Your swag version
1.16.6

Your go version
1.25.5

Desktop (please complete the following information):

  • OS: Windows
  • Browser: Firefox
  • Version: -

Additional context
Output from doc.json:

{
    "schemes": [],
    "swagger": "2.0",
    "host": "",
    "basePath": "/",
    "paths": {
        "/about": {
            "get": {
                "description": "Endpoint without explicit tags annotation",
                "produces": [
                    "application/json"
                ],
                "summary": "About endpoint",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/health": {
            "get": {
                "description": "Reference endpoint with a normal tag",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "reference"
                ],
                "summary": "Health check",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/tags-comma-only": {
            "get": {
                "description": "Endpoint using comma-only tags annotation",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "",
                    ""
                ],
                "summary": "Tags comma-only endpoint",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/tags-mixed": {
            "get": {
                "description": "Endpoint using mixed empty and non-empty tags annotation",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "tag1",
                    "",
                    "tag2"
                ],
                "summary": "Tags mixed endpoint",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        },
        "/tournaments/{id}": {
            "get": {
                "description": "Fake endpoint for local Swagger tag tests",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    ""
                ],
                "summary": "Get tournament by ID",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Tournament ID",
                        "name": "id",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "type": "object",
                            "additionalProperties": true
                        }
                    }
                }
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions