{"record":{"id":"e26f768ebfea0e25","repo":"pocketbase/pocketbase","slug":"geodistance-argument-d-must-be-an-identifier-or","errorCode":null,"errorMessage":"[geoDistance] argument %d must be an identifier or number","messagePattern":"\\[geoDistance\\] argument (.+?) must be an identifier or number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tools/search/token_functions.go","lineNumber":36,"sourceCode":"\t// distance between 2 points in kilometres (https://www.movable-type.co.uk/scripts/latlong.html).\n\t//\n\t// The accepted arguments at the moment could be either a plain number or a column identifier (including NULL).\n\t// If the column identifier cannot be resolved and converted to a numeric value, it resolves to NULL.\n\t//\n\t// Similar to the built-in SQLite functions, geoDistance doesn't apply\n\t// a \"match-all\" constraints in case there are multiple relation fields arguments.\n\t// Or in other words, if a collection has \"orgs\" multiple relation field pointing to \"orgs\" collection that has \"office\" as \"geoPoint\" field,\n\t// then the filter: `geoDistance(orgs.office.lon, orgs.office.lat, 1, 2) < 200`\n\t// will evaluate to true if for at-least-one of the \"orgs.office\" records the function result in a value satisfying the condition (aka. \"result < 200\").\n\t\"geoDistance\": func(argTokenResolverFunc func(fexpr.Token) (*ResolverResult, error), args ...fexpr.Token) (*ResolverResult, error) {\n\t\tif len(args) != 4 {\n\t\t\treturn nil, fmt.Errorf(\"[geoDistance] expected 4 arguments, got %d\", len(args))\n\t\t}\n\n\t\tresolvedArgs := make([]*ResolverResult, 4)\n\t\tfor i, arg := range args {\n\t\t\tif arg.Type != fexpr.TokenIdentifier && arg.Type != fexpr.TokenNumber {\n\t\t\t\treturn nil, fmt.Errorf(\"[geoDistance] argument %d must be an identifier or number\", i)\n\t\t\t}\n\t\t\tresolved, err := argTokenResolverFunc(arg)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"[geoDistance] failed to resolve argument %d: %w\", i, err)\n\t\t\t}\n\t\t\tresolvedArgs[i] = resolved\n\t\t}\n\n\t\tlonA := resolvedArgs[0].Identifier\n\t\tlatA := resolvedArgs[1].Identifier\n\t\tlonB := resolvedArgs[2].Identifier\n\t\tlatB := resolvedArgs[3].Identifier\n\n\t\treturn &ResolverResult{\n\t\t\tNullFallback: NullFallbackDisabled,\n\t\t\tIdentifier: `(6371 * acos(` +\n\t\t\t\t`cos(radians(` + latA + `)) * cos(radians(` + latB + `)) * ` +\n\t\t\t\t`cos(radians(` + lonB + `) - radians(` + lonA + `)) + ` +","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/pocketbase/pocketbase/blob/5d217ddb50cb144d80a5d0b0bdf11b52b2c3e457/tools/search/token_functions.go#L18-L54","documentation":"Validation error from `geoDistance`: each of the 4 arguments must be a filter-expr identifier (column reference) or a number token. Text literals, booleans, or other token types are rejected before resolution, because coordinates must map to numeric SQL values.","triggerScenarios":"Passing a quoted string as a coordinate, e.g. `geoDistance('office.lon', office.lat, 1, 2)`; passing a boolean or another function call as an argument.","commonSituations":"Quoting field paths out of habit from JSON usage; passing dynamic text values instead of numbers; confusion about identifier vs. literal syntax in the filter language.","solutions":["Remove quotes around field identifiers: `office.lon`, not `'office.lon'`","Express fixed coordinates as bare numbers, e.g. `26.0984`","Use only direct column references or numeric literals for all 4 arguments"],"exampleFix":"// before\nfilter := \"geoDistance('office.lon', 'office.lat', 26.1, 24.2) < 200\"\n// after\nfilter := \"geoDistance(office.lon, office.lat, 26.1, 24.2) < 200\"","handlingStrategy":"validation","validationCode":"// ensure geoDistance args are bare identifiers or numbers\nfor _, a := range args {\n    if !regexp.MustCompile(`^-?\\d+(\\.\\d+)?$`).MatchString(a) && !isIdentifierToken(a) {\n        return fmt.Errorf(\"geoDistance arg %q must be an identifier or number\", a)\n    }\n}","typeGuard":"null","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"[geoDistance] argument\") && strings.Contains(err.Error(), \"must be an identifier or number\") {\n    return apiError400(\"geoDistance arguments must be unquoted field paths or numeric literals\")\n}","preventionTips":["Never quote coordinate field paths in filters (office.lon, not 'office.lon')","Pass fixed coordinates as bare numbers","Document the filter grammar for clients building geoDistance filters"],"tags":["pocketbase","filter","geodistance","validation","search"],"backgroundTag":null,"analyzedSha":"5d217ddb50cb144d80a5d0b0bdf11b52b2c3e457","analyzedAt":"2026-08-15T10:06:33.165Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}