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
- Send the numeric unsigned ID in the path: /application/42, not /application/abc or /application/-1.
- Log and inspect the exact request URL — the offending segment is the path parameter named by the route.
- Fix client-side interpolation so the ID variable is defined and numeric before building the URL.
- If you only have a token or name, look up the numeric ID first via the corresponding list endpoint.
- 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
- Interpolate defined numeric variables into URL templates; guard against undefined/null becoming 'undefined'.
- Never send negative or decimal numbers — ParseUint rejects them.
- Remember the API expects numeric IDs only; slugs/UUIDs/name strings will always 400.
- On 32-bit hosts, keep IDs within 32-bit unsigned range.
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
- cannot delete internal application
- client not found
- application does not exist
- application does not exists
- message does not exist
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/d8f3d0f5d2c76804.
Report an issue: GitHub.