Skip to content

File upload fails: "Malformed OFISH capability probe output: unsupported status line" #40

Description

@CallMeJaja

Summary

Uploading a file fails with:

Malformed OFISH capability probe output: unsupported status line

The OFISH capability probe runs, but OfishCapabilityParser rejects the response. This happens against a self-hosted OpenCode 1.18.13 server using a custom OpenAI-compatible provider (9router → gemini flash medium) with the default build agent.

Expected

The probe returns the caps ... line followed by ### 200 ok (or ### 501 caps_missing ...), capabilities are cached, and file upload proceeds.

Actual

Upload is blocked. The error originates from OfishCapabilityParser.parse in app/src/main/java/dev/blazelight/p4oc/data/files/ofish/OfishCapabilityParser.kt, the else branch:

else -> OfishProbeResult.Failed("Malformed OFISH capability probe output: unsupported status line")

This means a caps line and a ### status line were both found and ordered correctly, but the final status line was neither ### 200 ok nor ### 501 caps_missing ....

Root-cause hypothesis

OfishShellOutputExtractor.extract (OfishCapabilityProbe.kt) concatenates all message parts into one blob:

message.parts.forEach { part ->
    part.text?.takeIf { it.isNotBlank() }?.let { appendLine(it) }
    part.state?.output?.takeIf { it.isNotBlank() }?.let { appendLine(it) }
    part.state?.raw?.takeIf { it.isNotBlank() }?.let { appendLine(it) }
    part.state?.error?.takeIf { it.isNotBlank() }?.let { appendLine(it) }
}

Then OfishCapabilityParser picks the status line with indexOfLast { it.startsWith("### ") }. On servers where the model appends assistant text after running the shell command — and LLMs routinely emit markdown level-3 headings like ### Note / ### Explanation — the last ### line is the model's prose, not the shell trailer, so parsing falls into the else branch.

Note the probe already has a clean precedent for this: extractMutationSegment deliberately treats shell tool state as authoritative and only falls back to text parts when no state contains the expected marker. The capability probe does not follow that pattern.

Suggested fix

  1. In OfishCapabilityProbe.probe(), prioritize state.output / state.raw (and ideally only the shell tool's state) over part.text — mirroring extractMutationSegment — so model prose can never shadow the shell trailer.
  2. Optionally make the parser more robust by matching only real FISH-style trailer lines, e.g. Regex("^### \\d{3}"), instead of any line starting with ### , and/or validate that the status line is the last non-empty line of the output.

Environment

  • P4OC latest main (at time of report)
  • Server: OpenCode 1.18.13 (opencode serve), custom OpenAI-compatible provider (9router), agent build, model gemini flash medium
  • Android app performing upload via SAF

Additional context

This may also be the same class of issue as the "malformed output" fragility in OfishMutationParser when non-shell prose gets interleaved — worth hardening both parsers against trailing model narrative.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions