Young Devs Bin
๐Ÿ“ Today I Learned

Gemini JSON Truncation, Font Scale Override in Compose, and Token Limits

ยท2 min readยท#today-i-learned#android#kotlin#compose#llm#gemini#ux
  • maxTokens cuts JSON mid-object. Gemini returns structured JSON ({"answer": "..."}) but if maxOutputTokens is too small, the response gets truncated before the closing }. lastIndexOf('}') returns -1, the JSON parse fails, and the raw incomplete string is shown as the answer. Fix: raise the limit (4096) or omit maxOutputTokens entirely so the model uses its default (8192 for Gemini 2.5 Flash).
  • gson.fromJson() with Kotlin data classes is fragile. Even with correct type declarations, Gson's reflection-based deserialization can silently return a default-constructed object when something unexpected is in the JSON. Replacing gson.fromJson(json, MyClass::class.java) with Android's built-in JSONObject(json).optString("answer", "") is simpler, has no generic type-erasure issues, and handles malformed input gracefully.
  • Fallback for truncated JSON: When lastIndexOf('}') == -1, a simple string walk โ€” find "answer", find :, find the opening ", read until the next " or end-of-string โ€” extracts whatever answer text was generated before the cutoff. Better than showing raw JSON.
  • Lock font scale for a data-visualization label in Compose:
    val d = LocalDensity.current
    CompositionLocalProvider(
        LocalDensity provides Density(density = d.density, fontScale = 1f)
    ) { Text(...) }
    This prevents the label from scaling with the system accessibility setting, keeping heatmap column widths consistent at any font size. Acceptable for decorative labels; don't use it for content the user needs to read.
  • LazyRow clips children by default. wrapContentWidth(unbounded = true) on a Text lets it paint wider than its layout slot, but the LazyRow clips everything at its own boundary. The only real fix for overflow is to constrain the child's layout size (not just its paint area).
  • A yellow warning box in Settings and onboarding is the right place to explain API token costs and response length limits โ€” it's visible at the moment the user is configuring the feature, not buried in docs.