gotify/server · error

invalid id

Error message

invalid id

What it means

withID is a Gin route-handler wrapper that parses a numeric path parameter via strconv.ParseUint and only invokes the wrapped handler on success. If the parameter is not a valid unsigned integer, it aborts with HTTP 400 and 'invalid id'. It guards all application/client mutation and read routes (DeleteApplication, UpdateApplication, UpdateApplicationSecurity, UploadApplicationImage, RemoveApplicationImage, UpdateClient, message routes, etc.).

Source

Thrown at api/internalutil.go:15

package api

import (
	"errors"
	"math/bits"
	"strconv"

	"github.com/gin-gonic/gin"
)

func withID(ctx *gin.Context, name string, f func(id uint)) {
	if id, err := strconv.ParseUint(ctx.Param(name), 10, bits.UintSize); err == nil {
		f(uint(id))
	} else {
		ctx.AbortWithError(400, errors.New("invalid id"))
	}
}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Send the numeric unsigned ID in the path: /application/42, not /application/abc or /application/-1.
  2. Log and inspect the exact request URL — the offending segment is the path parameter named by the route.
  3. Fix client-side interpolation so the ID variable is defined and numeric before building the URL.
  4. If you only have a token or name, look up the numeric ID first via the corresponding list endpoint.
  5. On 32-bit deployments, confirm IDs fit in 32 bits or run a 64-bit build.

Example fix

// before
fetch(`/application/${id}/update`, { method: 'PUT' }) // id === undefined -> /application/undefined/update -> 400 'invalid id'

// after
if (!Number.isInteger(id) || id < 0) throw new Error('application id must be a non-negative integer');
fetch(`/application/${id}/update`, { method: 'PUT' })
Defensive patterns

Strategy: validation

Validate before calling

// Validate the path ID before building any withID-wrapped URL
function assertPathId(name, value) {
  const n = Number(value);
  if (!Number.isInteger(n) || n < 0 || !Number.isSafeInteger(n)) {
    throw new TypeError(`${name} must be a non-negative integer, got: ${JSON.stringify(value)}`);
  }
  return n;
}
const id = assertPathId('application id', rawId); // throws early instead of HTTP 400

Type guard

function isUintId(v) {
  return typeof v === 'number' ? Number.isInteger(v) && v >= 0
    : typeof v === 'string' && /^\d+$/.test(v);
}

Try / catch

const res = await fetch(`/application/${id}/update`, { method: 'PUT', body });
if (res.status === 400) {
  const body = await res.text();
  if (body.includes('invalid id')) throw new Error(`path id '${id}' is not a valid unsigned integer`);
}

Prevention

When it happens

Trigger: Any withID-wrapped route called with a path parameter that fails ParseUint: a non-numeric value (e.g. /application/abc), a negative number (e.g. /client/-1 — minus sign is rejected for unsigned), a value exceeding uint range on 32-bit platforms, an empty segment (e.g. /application//update), or an ID with whitespace or URL-encoded characters.

Common situations: String template variables left unfilled ('${id}' literally in the URL); client-side code interpolating undefined/null (renders 'undefined' or empty); truncated URLs from bad redirect logic; assuming string slugs/UUIDs work instead of the numeric IDs this API expects; 32-bit builds overflowing large IDs.

Related errors


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