{"record":{"id":"6cd0e79c05fce928","repo":"thedotmack/claude-mem","slug":"invalid-ops-device-name-must-be-at-most-80-characters","errorCode":null,"errorMessage":"invalid_ops: device name must be at most 80 characters","messagePattern":"invalid_ops: device name must be at most 80 characters","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/do/SyncHub.ts","lineNumber":889,"sourceCode":"\n\tprivate setMeta(key: string, value: string): void {\n\t\tthis.ctx.storage.sql.exec(\n\t\t\t\"INSERT INTO meta (k, v) VALUES (?, ?) ON CONFLICT(k) DO UPDATE SET v=excluded.v\",\n\t\t\tkey,\n\t\t\tvalue,\n\t\t);\n\t}\n\n\tprivate deleteMeta(key: string): void {\n\t\tthis.ctx.storage.sql.exec(\"DELETE FROM meta WHERE k = ?\", key);\n\t}\n}\n\nfunction normalizeDeviceName(value: string | null): string | null {\n\tif (value === null) return null;\n\tconst normalized = value.trim();\n\tif (normalized.length === 0) return null;\n\tif (normalized.length > 80) throw invalid(\"device name must be at most 80 characters\");\n\treturn normalized;\n}\n\nfunction decimalLag(head: string, cursor: string): string {\n\tconst canonicalHead = assertCanonicalDecimal(head);\n\tconst canonicalCursor = assertCanonicalDecimal(cursor);\n\tif (compareCanonicalDecimals(canonicalCursor, canonicalHead) >= 0) return \"0\";\n\treturn (BigInt(canonicalHead) - BigInt(canonicalCursor)).toString(10);\n}\n","sourceCodeStart":871,"sourceCodeEnd":899,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/do/SyncHub.ts#L871-L899","documentation":"invalid(\"device name must be at most 80 characters\") is thrown by the module-level normalizeDeviceName when a provided device name trims to more than 80 characters. Null or whitespace-only names are normalized to null (no name), but any real name exceeding the cap is rejected rather than truncated.","triggerScenarios":"Registering or renaming a device with a display name longer than 80 characters after trimming; passing a user-entered label like a long browser user-agent string as the device name.","commonSituations":"Clients auto-filling device names from navigator.userAgent (often > 80 chars); iOS/Android device marketing names with model suffixes; copy-pasted long labels; failing to truncate before sending from a thin client.","solutions":["Truncate or shorten the device name to <= 80 characters before calling the API.","Use a short human label (e.g. \"MacBook Pro\") instead of the raw user-agent string.","Fall back to null (omit the name) when no concise label is available.","Trim first, then slice: value.trim().slice(0, 80) if truncation is acceptable."],"exampleFix":"// before\nregisterDevice(deviceId, navigator.userAgent); // usually > 80 chars\n// after\nconst name = shortDeviceLabel() ?? null; // e.g. \"Chrome on macOS\"\nregisterDevice(deviceId, name && name.slice(0, 80));","handlingStrategy":"validation","validationCode":"function assertValidDeviceName(name: string | null): string | null {\n  if (name === null) return null;\n  const n = name.trim();\n  if (n.length === 0) return null;\n  if (n.length > 80) throw new RangeError(`device name must be at most 80 characters, got ${n.length}`);\n  return n;\n}","typeGuard":"function isValidDeviceName(v: unknown): v is string | null {\n  return v === null || (typeof v === \"string\" && v.trim().length <= 80);\n}","tryCatchPattern":null,"preventionTips":["Never use navigator.userAgent as a device name; use a short friendly label.","Trim and cap names client-side: name.trim().slice(0, 80).","Send null instead of an empty or oversized name when no good label exists.","Add a maxLength=80 attribute to device-name form inputs."],"tags":["validation","length-limit","devices","input"],"backgroundTag":"value-out-of-range","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}