{"record":{"id":"0ec4e4ef32a727d7","repo":"thedotmack/claude-mem","slug":"invalid-ops-deviceid-must-be-1-128-characters","errorCode":null,"errorMessage":"invalid_ops: deviceId must be 1-128 characters","messagePattern":"invalid_ops: deviceId must be 1-128 characters","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"workers/sync-hub/src/do/SyncHub.ts","lineNumber":857,"sourceCode":"\t\tif (result.rowsWritten === 0) throw deviceLimitError();\n\t}\n\n\tprivate touchExistingDevice(deviceId: string, name: string | null, now = Date.now()): void {\n\t\tconst normalizedId = this.normalizeDeviceId(deviceId);\n\t\tthis.ctx.storage.sql.exec(\n\t\t\t`UPDATE devices\n\t\t\t SET name = COALESCE(name, ?), last_seen = ?\n\t\t\t WHERE device_id = ?`,\n\t\t\tname,\n\t\t\tnow,\n\t\t\tnormalizedId,\n\t\t);\n\t}\n\n\tprivate normalizeDeviceId(deviceId: string): string {\n\t\tconst normalizedId = deviceId.trim();\n\t\tif (normalizedId.length === 0 || normalizedId.length > 128) {\n\t\t\tthrow invalid(\"deviceId must be 1-128 characters\");\n\t\t}\n\t\treturn normalizedId;\n\t}\n\n\tprivate meta(key: string): string {\n\t\tconst row = this.ctx.storage.sql.exec<{ v: string }>(\"SELECT v FROM meta WHERE k = ?\", key).toArray()[0];\n\t\tif (!row) throw new Error(`sync-hub invariant: missing meta ${key}`);\n\t\treturn row.v;\n\t}\n\n\tprivate metaOptional(key: string): string | null {\n\t\treturn this.ctx.storage.sql.exec<{ v: string }>(\"SELECT v FROM meta WHERE k = ?\", key).toArray()[0]?.v ?? null;\n\t}\n\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,","sourceCodeStart":839,"sourceCodeEnd":875,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/workers/sync-hub/src/do/SyncHub.ts#L839-L875","documentation":"invalid(\"deviceId must be 1-128 characters\") is thrown by SyncHub.normalizeDeviceId when the trimmed deviceId is empty or longer than 128 characters. Device IDs are primary-key-like identifiers in the devices table, so the DO rejects any value outside the 1-128 character range before touching storage.","triggerScenarios":"Registering or touching a device with deviceId \"\" or only whitespace (trims to empty); a deviceId longer than 128 chars (e.g. a full URL, JWT, or concatenated UUID used as an ID); null/undefined coerced into a string form that fails the length check.","commonSituations":"Clients sending generated IDs from UUID+hostname+timestamp concatenation; framework code passing an empty string when the ID failed to load; copying an identifier with trailing whitespace and assuming trim is done server-side (it is, but emptiness after trim fails); migrating legacy IDs that exceed the cap.","solutions":["Ensure the client sends a non-empty deviceId of 1-128 characters after trimming.","Generate IDs within bounds, e.g. crypto.randomUUID() (36 chars) instead of long composite strings.","Trim/validate on the client before the call and show a field-level error.","If legacy IDs are too long, hash them (e.g. SHA-256 hex) to fit under 128 chars."],"exampleFix":"// before\nconst deviceId = `${userId}-${browser}-${window.location.href}`; // often > 128 chars\n// after\nconst deviceId = crypto.randomUUID(); // 36 chars, always valid","handlingStrategy":"validation","validationCode":"function assertValidDeviceId(deviceId: string): string {\n  const id = deviceId.trim();\n  if (id.length === 0 || id.length > 128) {\n    throw new RangeError(`deviceId must be 1-128 characters, got ${id.length}`);\n  }\n  return id;\n}","typeGuard":"function isValidDeviceId(v: unknown): v is string {\n  return typeof v === \"string\" && v.trim().length >= 1 && v.trim().length <= 128;\n}","tryCatchPattern":"try {\n  await registerDevice(deviceId, name);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"deviceId must be 1-128 characters\")) {\n    showFieldError(\"deviceId\", \"Must be 1-128 characters\");\n  } else throw e;\n}","preventionTips":["Generate IDs with crypto.randomUUID() instead of concatenating long strings.","Validate and trim IDs at the client boundary before network calls.","Never pass null/undefined into a string ID slot; check loading state first.","Document the 1-128 limit wherever device IDs are produced."],"tags":["validation","identifier","length-limit","devices"],"backgroundTag":"invalid-identifier-format","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"}