gotify/server · warning

cannot delete internal application

Error message

cannot delete internal application

What it means

This 400 error is returned by the DELETE /applications/:id endpoint when the application being deleted is marked as internal. Internal applications are system-managed records that the API forbids users from deleting, regardless of ownership. The check runs only after confirming the requester owns the application (app.UserID == auth.GetUserID(ctx)), so this error means 'you own it, but it is protected'.

Source

Thrown at api/application.go:197

//	    schema:
//	        $ref: "#/definitions/Error"
//	  403:
//	    description: Forbidden
//	    schema:
//	        $ref: "#/definitions/Error"
//	  404:
//	    description: Not Found
//	    schema:
//	        $ref: "#/definitions/Error"
func (a *ApplicationAPI) DeleteApplication(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) {
			if app.Internal {
				ctx.AbortWithError(400, errors.New("cannot delete internal application"))
				return
			}
			if success := successOrAbort(ctx, 500, a.DB.DeleteApplicationByID(id)); !success {
				return
			}
			if app.Image != "" {
				os.Remove(a.ImageDir + app.Image)
			}
		} else {
			ctx.AbortWithError(404, fmt.Errorf("app with id %d doesn't exists", id))
		}
	})
}

// UpdateApplication updates an application info by its id.
// swagger:operation PUT /application/{id} application updateApplication
//
// Update an application.

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Pick a different (non-internal) application to delete, or leave the internal one in place
  2. If the record truly must go, flip internal=false in the database (UPDATE applications SET internal=false WHERE id=...) and retry as an admin
  3. Filter internal applications out client-side before issuing DELETE calls (e.g. skip apps where internal is true)
  4. Patch the handler/seed so internal apps are not owned by regular users

Example fix

// before
apps.forEach(a => deleteApplication(a.id));
// after
apps.filter(a => !a.internal).forEach(a => deleteApplication(a.id));
Defensive patterns

Strategy: validation

Validate before calling

if (app.internal) { throw new SkipError('internal applications cannot be deleted'); }
await api.delete(`/applications/${app.id}`);

Type guard

function isDeletable(app) { return app != null && app.userid === currentUser.id && !app.internal; }

Prevention

When it happens

Trigger: Calling DELETE on an application whose DB record has internal=true. Typically the app was created by seed data, an admin, or a system bootstrap routine rather than by the user.

Common situations: Developers trying to clean up seed/demo applications; environments where an internal app got assigned to a user's account; scripts iterating over all owned applications and deleting each one, hitting the protected internal entry.

Related errors


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