Why
Operators currently see only an error code (DATA_APP_BUILD_FAILED or DATA_APP_DEPLOY_TIMEOUT) when kbagent data-app create --wait or kbagent data-app deploy --wait fails — the diagnostic content (the actual stderr/stdout from uv sync, missing-secret KeyErrors, etc.) is only visible in the Keboola UI's "Terminal Log" tab. This forces every troubleshooting session out of the terminal and breaks the CLI's contract of "diagnose from the CLI alone."
This was deferred from the v0.28.0 PR (feat/data-app-secrets-and-validate-repo) for a documented reason: the Data Science API does not expose Terminal Log content as JSON. Two in-repo references already commit to this:
src/keboola_agent_cli/services/data_app_service.py — the existing _poll_until_terminal() raises DATA_APP_BUILD_FAILED with the message "the Data Science API does not expose it as JSON".
plugins/kbagent/skills/kbagent/references/data-app-workflow.md "What this command group deliberately does NOT cover" — "the Data Science API does not expose Terminal Logs as JSON".
Help canon confirms: https://help.keboola.com/data-apps/terminal-log-tab/ documents only an in-UI "Download Logs" button, no programmatic API.
Verification needed
Before this issue can become a scoped implementation ticket, the §1b live probe needs to run against a real Keboola project's Data Science API:
# Against an app that has just hit state == error
TOKEN=$KBC_TOKEN
STACK=https://data-science.keboola.com # adjust per stack
curl -s -o /dev/null -w "%{http_code}\n" -H "X-StorageApi-Token: $TOKEN" "$STACK/apps/{id}/logs"
curl -s -o /dev/null -w "%{http_code}\n" -H "X-StorageApi-Token: $TOKEN" "$STACK/apps/{id}/logs?stream=runtime"
curl -s -o /dev/null -w "%{http_code}\n" -H "X-StorageApi-Token: $TOKEN" "$STACK/apps/{id}/events"
# Plus inspect the UI's network tab during a "Download Logs" click — whatever endpoint that hits is the canonical answer.
Outcomes:
- All 404 / 403 → this issue becomes a Keboola engineering feature request: please expose a programmatic logs endpoint (could be JSON, plaintext stream, or a presigned URL behind an authenticated GET). This blocks the CLI side.
- Any 200 with a usable body shape → this issue becomes an implementation ticket for
kbagent data-app logs with the verified shape locked in.
Implementation blueprint (Option B from the v0.28.0 PR plan)
If/when an endpoint becomes available:
- New client method
DataScienceClient.get_app_logs(app_id, *, stream='build|runtime', since=None, tail=None) — streaming httpx.Response body iteration so --follow doesn't OOM.
- Service
fetch_data_app_logs(...) returns either a dict (final) or yields lines (follow mode) via a generator. Match the existing --log-tail-lines precedent for the attached-on-error case.
- Command flags
--tail N (default 50 in auto-dump, default 200 standalone), --follow, --since DURATION, --build-only, --runtime-only, --json.
- SIGINT handling: flush, close connection, exit 130. Ensure
httpx.Response.close() runs in a finally even when the generator is interrupted mid-yield.
- Auto-dump: in
_poll_until_terminal(), on state == TERMINAL_ERROR_STATE, fetch tail=N (default 50) before raising; attach to KeboolaApiError.details["logTail"]. Behind a --no-logs-on-failure opt-out for scripted callers.
- Permission:
data-app.logs = read.
- New
(since vX.Y.Z) gotchas.md entry: "deploy hang >5min — kbagent data-app logs <id> --tail 100 is faster than the UI".
Sync-map updates required when this lands
kbagent context AGENT_CONTEXT
CLAUDE.md ## All CLI Commands
keboola-expert.md matrix (new row) and Rule 6 VERSION GATE
commands-reference.md (new bullet)
gotchas.md (new since-tagged entry)
data-app-workflow.md — replace the <TODO: link after filing> deferral note with a "see implementation in PR #X" reference, drop the "deliberately does NOT cover the build/runtime log" entry once shipped
Out of scope for this issue
- Non-
python-js backends (Streamlit / R / Node-only).
- Live tailing via WebSocket (only HTTP-poll documented at time of writing).
- Streaming logs to a file rotated by size.
Tracked-from PR
Filed alongside padak/keboola_agent_cli PR #X = v0.28.0 data-app secrets-* + validate-repo. The PR description's "Out of scope" section links back to this issue.
Why
Operators currently see only an error code (
DATA_APP_BUILD_FAILEDorDATA_APP_DEPLOY_TIMEOUT) whenkbagent data-app create --waitorkbagent data-app deploy --waitfails — the diagnostic content (the actual stderr/stdout fromuv sync, missing-secret KeyErrors, etc.) is only visible in the Keboola UI's "Terminal Log" tab. This forces every troubleshooting session out of the terminal and breaks the CLI's contract of "diagnose from the CLI alone."This was deferred from the v0.28.0 PR (
feat/data-app-secrets-and-validate-repo) for a documented reason: the Data Science API does not expose Terminal Log content as JSON. Two in-repo references already commit to this:src/keboola_agent_cli/services/data_app_service.py— the existing_poll_until_terminal()raisesDATA_APP_BUILD_FAILEDwith the message "the Data Science API does not expose it as JSON".plugins/kbagent/skills/kbagent/references/data-app-workflow.md"What this command group deliberately does NOT cover" — "the Data Science API does not expose Terminal Logs as JSON".Help canon confirms: https://help.keboola.com/data-apps/terminal-log-tab/ documents only an in-UI "Download Logs" button, no programmatic API.
Verification needed
Before this issue can become a scoped implementation ticket, the §1b live probe needs to run against a real Keboola project's Data Science API:
Outcomes:
kbagent data-app logswith the verified shape locked in.Implementation blueprint (Option B from the v0.28.0 PR plan)
If/when an endpoint becomes available:
DataScienceClient.get_app_logs(app_id, *, stream='build|runtime', since=None, tail=None)— streaminghttpx.Responsebody iteration so--followdoesn't OOM.fetch_data_app_logs(...)returns either adict(final) or yields lines (follow mode) via a generator. Match the existing--log-tail-linesprecedent for the attached-on-error case.--tail N(default 50 in auto-dump, default 200 standalone),--follow,--since DURATION,--build-only,--runtime-only,--json.httpx.Response.close()runs in afinallyeven when the generator is interrupted mid-yield._poll_until_terminal(), onstate == TERMINAL_ERROR_STATE, fetchtail=N(default 50) before raising; attach toKeboolaApiError.details["logTail"]. Behind a--no-logs-on-failureopt-out for scripted callers.data-app.logs=read.(since vX.Y.Z)gotchas.mdentry: "deploy hang >5min —kbagent data-app logs <id> --tail 100is faster than the UI".Sync-map updates required when this lands
kbagent contextAGENT_CONTEXTCLAUDE.md## All CLI Commandskeboola-expert.mdmatrix (new row) and Rule 6 VERSION GATEcommands-reference.md(new bullet)gotchas.md(new since-tagged entry)data-app-workflow.md— replace the<TODO: link after filing>deferral note with a "see implementation in PR #X" reference, drop the "deliberately does NOT cover the build/runtime log" entry once shippedOut of scope for this issue
python-jsbackends (Streamlit / R / Node-only).Tracked-from PR
Filed alongside
padak/keboola_agent_cliPR #X = v0.28.0 data-app secrets-* + validate-repo. The PR description's "Out of scope" section links back to this issue.