Direct answer
“Empty output” must be divided into observable stages before the app retries. The user may have submitted no usable input; preprocessing may have produced an empty payload; the rendered prompt may contain only control structure; the runtime may return no text or terminate early; the stream collector may not observe an emission; the parser may reject or discard a result; or the UI may render the wrong state. These are separate failures. Capture the earliest available evidence for each boundary using a fixed non-sensitive fixture, and do not treat an empty screen as proof that the model generated an empty response. Create a task trace with request ID, artifact/runtime identity, prompt/template revision, input modality, preprocessing outcome, runtime start, each emitted chunk if exposed, terminal/stop state, raw final output if safe in test fixtures, parser outcome, and UI state. In production, do not store user prompts or outputs merely to debug a blank result; keep stage codes, lengths, and error classes. The LiteRT-LM Kotlin guide demonstrates conversation calls and collection of streaming output, but the exact integration must be inspected to establish what its wrapper emits and how cancellation is represented. Do not invent an event or stop API. Use controls that distinguish pipeline stages. A deterministic local test fixture with known non-empty expected properties can verify the app state path. A no-input case checks validation. A deliberately rejected structured result tests parser behavior. A cancellation fixture confirms that the UI does not convert “cancelled before first chunk” into “model returned empty.” A successful runtime call with an empty final string should remain different from a call that never reached native execution. Preserve timeout and failure classifications as well. Check state transitions and one-request ownership. A stale collector can overwrite a newer result with an empty state; a lifecycle-aware collector may stop observing while native work continues; a retry can replace request state before the first request finishes. Compare request IDs and task owners, confirm each result is applied only to the current request, and verify that stream completion and UI completion are not confused. If output exists before parsing but not after, investigate schema validation or stop-token removal. If parsed content exists but the view is blank, inspect view-state mapping and localization fallback. Finally, make retry policy specific to the diagnosed stage. Empty input should prompt for input, missing asset should direct repair, runtime failure should expose a classified action, and a genuinely empty model response should be recorded as an output property. Avoid endless retries that consume resources or duplicate work. This guide is a debugging protocol, not a claim about an output rate or model quality; report only what is observed for the exact fixture, app, artifact, runtime, and task.
The screen is blank although the user believes generation completed
A local assistant shows no text after a request. The app recently changed stream collection and parser validation. There is no evidence yet whether native output was emitted or the screen simply lost the result during state restoration.
Decision
Trace one fixed request through input, runtime, emission, stop, parser, and view state with stable request identity before enabling retry.
Expected outcome
The team classifies the first empty boundary and provides a stage-specific recovery without replaying private input blindly.
Evidence to collect
The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:
- Stage transition trace
- Known non-empty fixture
- Raw and parsed output states
A release claim is valid only for the recorded configuration and acceptance set. Generalization requires additional records.
Implementation workflow
- Create one fixed non-empty control. Use a short synthetic task with explicit expected output properties, plus a no-input control and a parser-rejection control. Freeze artifact, runtime, prompt, app build, and request ID for the trace.
- Trace input and rendered prompt. Record whether input was present, preprocessing accepted it, and the rendered request contains the expected content. Do not infer internal tokens or context availability from character counts.
- Observe runtime and stream lifecycle. Record native-call start, exposed chunks, terminal state, cancellation, timeout, and error stage only when the integration provides them. Distinguish no emission from an empty final response and from cancellation before first output.
- Compare raw, parsed, and rendered result. In a synthetic test, compare safe raw output with parser result and UI state. Check stop handling, schema rejection, request ID ownership, stale collectors, and localization/view-state fallbacks.
- Apply stage-specific retry behavior. Prompt for missing input, repair missing assets, report runtime initialization errors, and record a genuine empty model response separately. Bound retries and ensure a newer request cannot be overwritten by an older result.
Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.
Acceptance criteria
- A blank UI, empty final output, no stream emission, parser rejection, cancellation, and runtime failure are distinct states.
- Each result is tied to a request ID and the exact artifact/runtime/prompt fixture; stale tasks cannot overwrite newer UI state.
- Retry is triggered only by a classified recoverable stage and is bounded rather than silently repeated.
- Production diagnostics use redacted stage metadata rather than retaining private prompts or generated text.
Decision table
| Dimension | Choice or evidence | How to interpret it |
|---|---|---|
| No runtime emission | Inspect call start, collection, cancellation, timeout, and error state | The app may never have received output; do not label it an empty model answer. |
| Raw output exists, parser is empty | Inspect schema, stop filtering, and parse rejection | A pipeline transformation removed or rejected content after inference. |
| Parsed result exists, screen blank | Inspect request ownership and UI state mapping | The display path may lose valid content independently of runtime output. |
Failure patterns to prevent
- Treating empty UI as empty model output
- Retrying without stage evidence
Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.
Minimum reproducibility record
| Layer | Record |
|---|---|
| Device | Manufacturer, model, chipset, RAM class, operating-system build |
| Software | Application version and git commit |
| Model | Family, variant, revision, format, file length, hash, quantization |
| Runtime | Name, revision, requested backend, observed backend evidence |
| Workload | Fixture revision, input hash, prompt hash, output policy |
| Outcome | Completed, failed, cancelled, fallback, and privacy-safe diagnostics |
Release checklist
- ☐ The primary query is answered without an unsupported number.
- ☐ Every artifact and runtime is pinned.
- ☐ The representative task and failure policy are explicit.
- ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
- ☐ User-content and network boundaries are documented.
- ☐ Result wording applies only to the recorded configuration.
- ☐ The page links to raw method or evidence when results are added.
Sources and related evidence
- Cove benchmark method
- LiteRT-LM official Kotlin conversation and streaming guide
- Kotlin Flow collection and cancellation guide
Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.
Frequently asked questions
Does a blank screen prove the model returned an empty string?
No. Input validation, runtime invocation, stream observation, cancellation, parser rejection, and UI mapping can all yield a blank screen. Compare the earliest safe observable boundary.
Should the app automatically retry every empty result?
No. A retry is appropriate only for a classified recoverable stage. Missing input, parser rejection, timeout, and a genuine empty output need different user actions and retry rules.
Can I log the full prompt to debug this?
Prefer a synthetic fixture and redacted stage metadata. Production prompts and generated text may be private; full content is not needed to determine whether the call started or the parser rejected a result.