Skip to content

Reporting Issues

When SAM misbehaves, you can report it without leaving the app. This page covers the reporting flow for everyday users, then the operator-side setup that makes it work.

There are two ways in.

Use this when an agent did something wrong — got stuck, produced bad output, lost its work, failed to start.

The Report action lives in the session header’s expanded detail panel, so open that first:

  1. In the chat, click the chevron at the right of the session header (Show session details).
  2. In the action row that appears, click Report (the flag icon).

That row also has Files, Git, and Workspace while the session is live; those disappear once it stops or fails. Report and Timeline stay put — so you can still report a session that already ended, which is usually when you want to.

The Report an Issue dialog in SAM: a Title field, a Description field, and a checked "Attach technical references to help diagnose this issue" checkbox listing a Chat session, Task, and Node identifier.

Shown with the consent box ticked so the identifier list is visible. It is unchecked by default.

If the UI itself crashes, the error screen offers a Report this issue link. Use this when a page went blank or threw an error rather than the agent misbehaving.

Either way you get the same dialog: a title, a description, and — when SAM has context to offer — a consent checkbox.

What gets attached, and only if you say so

Section titled “What gets attached, and only if you say so”

SAM never attaches technical context silently. The dialog shows an unchecked box:

Attach technical references to help diagnose this issue

Tick it and SAM lists the exact identifiers it would send, so you can see them before you submit:

ReferenceWhere it comes fromChecked against your access?
Chat sessionThe session you’re reporting fromYes — must be your workspace
TaskThe task backing that sessionYes — must be your project
NodeThe machine the workspace was running onYes — must be your node
ErrorThe crash screen’s error textNo — see below

Reporting from a chat session offers the first three; the crash screen offers the error reference. (The API also accepts a Diagnosis reference for a superadmin deployment diagnosis, but no screen currently supplies one.)

These are identifiers only — SAM does not ship your code, your transcript, or your environment. They let a maintainer look up the right records rather than guess from a description.

The Error reference is the odd one out. It is taken from the crash text your browser produced, but the server only accepts identifier-shaped values — letters, digits, and . _ : -, with no spaces. Most real error messages contain spaces or punctuation, so in practice this reference is usually dropped rather than attached. Describe the error in your own words; don’t count on it coming along.

Leave the box unchecked and only your title and description are submitted. The report still gets filed.

Two things happen server-side regardless of what you tick:

  • Ownership is re-checked. Session, task, node, and diagnosis references are verified against your access before they’re stored, and silently dropped if you don’t have it — a stale or copied identifier can’t pull someone else’s records into your report. The error reference is not looked up, because it isn’t a pointer to a stored record. The confirmation screen lists what was actually attached, which may be fewer references than the dialog offered — and lists none at all if everything was dropped.
  • Everything is scrubbed — your title, your description, and each attached reference. Best-effort redaction strips credential-shaped strings (API keys, tokens, sk-/ghp_-style prefixes, PEM private key blocks) and email addresses before anything is stored, so a secret that leaked into an error message doesn’t ride along. Treat it as a safety net, not a licence — don’t paste secrets into the box.

You get a confirmation with a report ID, a status of draft, and the list of references that were actually attached. The report becomes a draft Idea in the platform’s feedback project, where a maintainer picks it up.

Reports are rate-limited to 20 per hour per account (RATE_LIMIT_REPORT_ISSUE_POST). Exceeding that returns a 429. The window is a fixed clock hour rather than a rolling sixty minutes, so the allowance resets at the top of the next hour — which may be in five minutes or fifty-five.

Length limits: title 200 characters, description 5,000 characters. The dialog enforces both as you type, so you’ll notice the cap rather than lose text on submit.

The feature is hidden entirely unless the deployment has been configured with a feedback project. If neither entry point appears, this deployment hasn’t set one up — report through whatever channel your operator uses instead. On a self-hosted SAM, that operator is you; see below.


Reports are filed as draft Ideas in one project you nominate:

PLATFORM_FEEDBACK_PROJECT_ID=<project id>

Set it to a project that exists in this deployment’s database. SAM validates the project on every check — if the variable is unset, or points at a project that doesn’t exist, GET /api/report-issue/config returns enabled: false and both entry points disappear from the UI. This is deliberate: an unconfigured deployment shows no button rather than a button that errors.

The same project also receives automated error triage, so pick one you’ll actually watch — a dedicated “Platform Feedback” project works well.

VariableDefaultDescription
PLATFORM_FEEDBACK_PROJECT_IDunsetProject that receives reports and triage Ideas. Unset ⇒ feature hidden.
REPORT_ISSUE_TITLE_MAX_LENGTH200Truncation ceiling for the stored title — see below
REPORT_ISSUE_DESCRIPTION_MAX_LENGTH5000Truncation ceiling for the stored description — see below
REPORT_ISSUE_CONTENT_MAX_LENGTH65536Max stored Idea body, including attached references
RATE_LIMIT_REPORT_ISSUE_POST20Report submissions allowed per clock hour, per user

Every report Idea is written in a three-part structure that keeps maintainer instructions separate from user-supplied text:

SectionContents
## Maintainer InstructionsSAM’s own triage instructions, plus an explicit security boundary statement
## Trusted MetadataThe identifiers the reporter consented to attach, one per line
## Untrusted Evidence: User Report DescriptionThe reporter’s own words, redacted and wrapped in a Markdown code fence

That fence matters when you point an agent at the resulting Idea. The reporter’s description is external input, and treating it as instructions would be a prompt-injection channel straight into a maintainer’s agent. The format tells the agent explicitly which parts are yours and which are the reporter’s. SAM’s default “execute idea” prompt template carries the same warning; if you override it with VITE_EXECUTE_IDEA_PROMPT_TEMPLATE, keep that instruction.

Beyond user-submitted reports, SAM files its own reports. Once an hour it groups recent platform errors, runs the deployment diagnosis agent on a representative error from each group, and writes a draft Idea into the same feedback project.

Grouping is by a redacted content signature, so a recurring error updates its existing Idea instead of filing a new one every hour. A group that fails triage repeatedly is rejected rather than retried forever.

There is no UI button for triage yet. A superadmin can POST /api/admin/observability/feedback-triage to sweep immediately rather than waiting for the next hourly run. Note that a manual sweep still only looks back over PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES (60 minutes by default), so it will not surface older errors — to test a fresh configuration, trigger it while a recent error is still inside that window.

VariableDefaultDescription
PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES60Lookback window for grouping recent errors
PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT100Max error rows scanned per sweep
PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT5Max grouped candidates processed per sweep
PLATFORM_FEEDBACK_TRIAGE_EVIDENCE_LIMIT10Max error references retained per group
PLATFORM_FEEDBACK_TRIAGE_CLAIM_TTL_MS600000Claim lease before a later sweep can reclaim a group
PLATFORM_FEEDBACK_TRIAGE_MAX_FAILURES3Failed attempts before a group is rejected
PLATFORM_FEEDBACK_TRIAGE_FAILURE_REASON_MAX_LENGTH240Max characters stored for a sanitized failure reason

Automated triage and superadmin-initiated diagnosis have separate daily token budgets. They read the same DEBUG_AGENT_DAILY_TOKEN_LIMIT value but count against independent per-feature counters, so a noisy hour of triage can never eat the allowance a superadmin wants for hands-on diagnosis. Budget accordingly: with triage enabled, worst-case daily spend on diagnosis is twice DEBUG_AGENT_DAILY_TOKEN_LIMIT. To cap triage specifically, lower PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT or PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT.

For superadmins: diagnosing errors with an agent

Section titled “For superadmins: diagnosing errors with an agent”

Admin → Errors can hand an error to an AI agent for analysis.

  • Diagnose on a single error row — analyze one specific failure.
  • Diagnose window — analyze everything in the current filter. Windows longer than 24 hours are clamped, and the button relabels itself Diagnose latest 24h so you know what you’re actually getting.

The agent reads bounded, redacted evidence — recent errors, a health summary, error trends, Worker logs, and related entity state — and returns a written analysis. The panel shows the model, turn count, tokens for that run, and your usage against the daily budget.

Diagnosis runs are durable. Starting one returns immediately and the work continues server-side, so closing the tab doesn’t kill it. A Recent diagnosis runs card lists the last several runs with their status (queued, running, succeeded, failed, cancelled); an in-flight run is marked recoverable after refresh and can be cancelled at a durable checkpoint. Failed runs can be retried from the card, while failed or cancelled runs can be retried from their detail page.

When a diagnosis is worth keeping, Save as draft Idea files it into a project you choose so it becomes tracked work instead of a panel you have to leave open.

/admin/errors is superadmin-only and its raw rows can contain local user IDs, IP addresses, and user-agent strings. Before any tool result reaches the model, SAM recursively strips those fields plus credential-shaped values — API tokens, JWTs, authorization headers, private keys, and long secret-like strings. Cloudflare credentials stay server-side and never enter model messages or saved diagnosis text.

The same redactor now also runs on the Worker log query behind /admin/logs, over each entry’s details object. So a superadmin browsing logs directly will see [REDACTED] in place of values — including user_id, ip_address, and user_agent, which are correlation fields rather than secrets. That is expected, not a bug. It does not cover an entry’s message text, so treat log messages as unredacted.

For VM Agent failures, the error row can include a Diagnostic evidence card. The VM Agent assigns one stable incident ID, durably queues the error, and collects a small same-installation snapshot while it retries delivery. The snapshot is deliberately narrower than a debug package:

  • allowlisted runtime health, agent version, bounded system resources, structured event metadata, and workspace lifecycle state;
  • recursive credential-shaped value redaction plus depth, item, string, document, archive, spool, and retention limits;
  • no repository files, arbitrary filesystem reads, environment dumps, shell history, raw command output, session transcript, or cross-installation transport.

The redacted preview is stored in D1. Compressed bytes remain in the deployment’s private R2 bucket and are streamed only through a superadmin-authenticated download route; SAM never returns the R2 object key or a direct object URL to the browser or diagnosis model. The card shows pending, available, failed, expired, or missing explicitly, including collector failures and truncation/redaction counts. A pending upload is not presented as complete.

The diagnosis agent gets only the bounded redacted preview through its read-only incident tool. Downloading the private archive is a separate human action. Collect debug package remains a separate, explicit, broader live-node action and is never run automatically or exposed to the diagnosis model.

VariableDefaultDescription
DEBUG_AGENT_MODEL@cf/zai-org/glm-5.2Workers AI model used for diagnosis
DEBUG_AGENT_MAX_TURNS6Max model/tool turns per diagnosis
DEBUG_AGENT_RUN_TOKEN_LIMIT24000Combined token ceiling per diagnosis
DEBUG_AGENT_MODEL_OUTPUT_TOKENS4096Max output tokens per model turn
DEBUG_AGENT_DAILY_TOKEN_LIMIT120000Daily budget, counted per feature — manual diagnosis and automated triage each get this much
DEBUG_AGENT_TOOL_RESULT_LIMIT50Max rows returned by a diagnosis tool
DEBUG_AGENT_TOOL_RESULT_BYTES32768Max serialized bytes per model-visible result
DEBUG_AGENT_MAX_WINDOW_HOURS24Max selectable diagnosis window
DEBUG_AGENT_TIMEOUT_MS120000Timeout per diagnosis model request
DEBUG_AGENT_HARD_DEADLINE_MS900000Hard deadline after which an active diagnosis is terminalized
DEBUG_AGENT_STALE_HEARTBEAT_MS120000Stale heartbeat threshold used by the orphan reconciler
DEBUG_AGENT_RETRY_BASE_DELAY_MS2000Initial classified transient retry delay
DEBUG_AGENT_RETRY_MAX_DELAY_MS60000Maximum classified transient retry delay
DEBUG_AGENT_STEP_MAX_RETRIES3Maximum transient retries for one checkpointed step

Exhausting the daily budget does not block the request up front — the run is accepted, then fails with “Daily deployment debugging budget exhausted” and appears in Recent diagnosis runs as failed. Its Retry button will keep failing until the next day.

See the Configuration Reference for the complete variable list.