gotify/server · error

file with key 'file' must be present

Error message

file with key 'file' must be present

What it means

The image-upload endpoint requires a multipart form field named exactly 'file'. When the multipart request contains no file under that key, gin's ctx.FormFile returns http.ErrMissingFile and the handler converts it into this 400 error. It is the API's way of saying the required upload part is absent.

Source

Thrown at api/application.go:417

//	        $ref: "#/definitions/Error"
//	  404:
//	    description: Not Found
//	    schema:
//	        $ref: "#/definitions/Error"
//	  500:
//	    description: Server Error
//	    schema:
//	        $ref: "#/definitions/Error"
func (a *ApplicationAPI) UploadApplicationImage(ctx *gin.Context) {
	withID(ctx, "id", func(id uint) {
		app, err := a.DB.GetApplicationByID(id)
		if success := successOrAbort(ctx, 500, err); !success {
			return
		}
		if app != nil && app.UserID == auth.GetUserID(ctx) {
			file, err := ctx.FormFile("file")
			if err == http.ErrMissingFile {
				ctx.AbortWithError(400, errors.New("file with key 'file' must be present"))
				return
			} else if err != nil {
				ctx.AbortWithError(500, err)
				return
			}
			head := make([]byte, 261)
			open, _ := file.Open()
			open.Read(head)
			if !filetype.IsImage(head) {
				ctx.AbortWithError(400, errors.New("file must be an image"))
				return
			}

			ext := filepath.Ext(file.Filename)
			if !ValidApplicationImageExt(ext) {
				ctx.AbortWithError(400, errors.New("invalid file extension"))
				return
			}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Send the file in multipart/form-data under the exact key 'file' (FormData.append('file', file))
  2. With curl use: curl -F "file=@/path/to/img.png" ...
  3. Verify the request Content-Type is multipart/form-data and the body is not JSON
  4. Check client code/libraries that the field name was not renamed

Example fix

// before
const fd = new FormData();
fd.append('image', file);
fetch('/applications/1/image', {method:'POST', body: fd});
// after
const fd = new FormData();
fd.append('file', file);
fetch('/applications/1/image', {method:'POST', body: fd});
Defensive patterns

Strategy: validation

Validate before calling

const fd = new FormData();
if (!file) throw new Error('a file is required');
fd.append('file', file, file.name);
console.log(fd.get('file')); // must be non-null under key 'file'

Try / catch

try {
  await api.post(`/applications/${id}/image`, fd);
} catch (e) {
  if (e.response?.status === 400 && e.response?.data?.includes("must be present")) {
    // ensure multipart part is named 'file' and retry
  } else throw e;
}

Prevention

When it happens

Trigger: POSTing to the upload route with multipart/form-data that lacks a part named 'file'; sending JSON instead of multipart; sending the file under a different field name such as 'image' or 'upload'; sending an empty file part.

Common situations: Frontend FormData.append('image', f) instead of append('file', f); using curl -F without the right key; clients sending application/json bodies; axios default content-type overriding multipart; testing tools omitting the file.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/79f2d1563cd54e98. Report an issue: GitHub.