{"record":{"id":"27a2e6fe8d050272","repo":"multica-ai/multica","slug":"move-position-is-out-of-range","errorCode":null,"errorMessage":"move position is out of range","messagePattern":"move position is out of range","errorType":"http","errorClass":null,"httpStatus":409,"severity":"warning","filePath":"server/internal/handler/issue_move.go","lineNumber":205,"sourceCode":"\treturn &position, true\n}\n\nfunc issueMovePosition(current float64, before, after *float64) (float64, error) {\n\tswitch {\n\tcase before != nil && after != nil:\n\t\tif !(*before < *after) {\n\t\t\treturn 0, errors.New(\"move anchors are stale or out of order\")\n\t\t}\n\t\tposition := *before + (*after-*before)/2\n\t\tif !(position > *before && position < *after) ||\n\t\t\tmath.IsInf(position, 0) || math.IsNaN(position) {\n\t\t\treturn 0, errors.New(\"move anchors are too close; refresh and retry\")\n\t\t}\n\t\treturn position, nil\n\tcase before != nil:\n\t\tposition := *before + 1\n\t\tif math.IsInf(position, 0) || math.IsNaN(position) {\n\t\t\treturn 0, errors.New(\"move position is out of range\")\n\t\t}\n\t\treturn position, nil\n\tcase after != nil:\n\t\tposition := *after - 1\n\t\tif math.IsInf(position, 0) || math.IsNaN(position) {\n\t\t\treturn 0, errors.New(\"move position is out of range\")\n\t\t}\n\t\treturn position, nil\n\tdefault:\n\t\treturn current, nil\n\t}\n}\n","sourceCodeStart":187,"sourceCodeEnd":218,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/handler/issue_move.go#L187-L218","documentation":"issueMovePosition's single-anchor branches append with a fixed step: position = before + 1 (move below the anchor) or position = after - 1 (move above it). If that arithmetic overflows to ±Inf or produces NaN — which requires the anchor to be at or near the float64 maximum (or minimum for the subtract branch) — the result is rejected as out of range. In normal operation positions stay near zero, so hitting this indicates corrupted or adversarial position values, not ordinary use.","triggerScenarios":"Move with a single before anchor whose stored position is close to math.MaxFloat64 (so +1 rounds to the same value or the value is already Inf), or an after anchor near -MaxFloat64 / equal to -Inf (so -1 yields Inf/NaN behavior). Realistically only reachable via corrupted rows or directly crafted API calls.","commonSituations":"A bug or manual SQL write leaving an issue position at an extreme value, then dragging relative to it; fuzzing or pen-testing the move endpoint with extreme floats; NaN positions inserted through some other path and used as an anchor.","solutions":["Identify the corrupted row: query for positions that are NULL, NaN, or outside a sane band (e.g. |pos| > 1e15) and reset them to normal values.","Trigger a position rebalance/normalization for the affected list so all positions return to a small range.","Guard client-side: refuse to use an anchor whose position is not a finite, modestly-sized number.","If caused by writes from another tool, fix that tool to emit bounded positions."],"exampleFix":"// before (corrupted row)\nUPDATE issue SET position = 1e308 WHERE id = '...';\n\n// after\nUPDATE issue SET position = 0 WHERE id = '...'; -- or run the list rebalance job","handlingStrategy":"validation","validationCode":"const POS_BOUND = 1e15;\n\nfunction assertAnchorSane(anchor) {\n  if (!anchor) return;\n  const p = anchor.position;\n  if (!Number.isFinite(p) || Math.abs(p) > POS_BOUND) {\n    throw new Error(`anchor ${anchor.id} has corrupt position ${p}; refetch or rebalance the list`);\n  }\n}","typeGuard":"const isSanePosition = (p) => Number.isFinite(p) && Math.abs(p) < 1e15;","tryCatchPattern":null,"preventionTips":["Never write positions from external tools without bounding them to a sane numeric range.","Add a monitoring query for |position| > 1e15 or non-finite positions to catch corruption early.","Run the list's rebalance/normalization job when corrupted positions are detected."],"tags":["ordering","float-overflow","data-corruption","guard"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}