{"record":{"id":"caab45749e7feb69","repo":"charmbracelet/crush","slug":"message-content-is-shorter-than-read-bytes-d","errorCode":null,"errorMessage":"message content is shorter than read bytes: %d < %d","messagePattern":"message content is shorter than read bytes: (.+?) < (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/app/app.go","lineNumber":416,"sourceCode":"\t\t\t\tif errors.Is(result.err, context.Canceled) || errors.Is(result.err, agent.ErrRequestCancelled) {\n\t\t\t\t\tslog.Debug(\"Non-interactive: agent processing cancelled\", \"session_id\", sess.ID)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"agent processing failed: %w\", result.err)\n\t\t\t}\n\t\t\treturn nil\n\n\t\tcase event := <-messageEvents:\n\t\t\tmsg := event.Payload\n\t\t\tif msg.SessionID == sess.ID && msg.Role == message.Assistant && len(msg.Parts) > 0 {\n\t\t\t\tstopSpinner()\n\n\t\t\t\tcontent := msg.Content().String()\n\t\t\t\treadBytes := messageReadBytes[msg.ID]\n\n\t\t\t\tif len(content) < readBytes {\n\t\t\t\t\tslog.Error(\"Non-interactive: message content is shorter than read bytes\", \"message_length\", len(content), \"read_bytes\", readBytes)\n\t\t\t\t\treturn fmt.Errorf(\"message content is shorter than read bytes: %d < %d\", len(content), readBytes)\n\t\t\t\t}\n\n\t\t\t\tpart := content[readBytes:]\n\t\t\t\t// Trim leading whitespace. Sometimes the LLM includes leading\n\t\t\t\t// formatting and intentation, which we don't want here.\n\t\t\t\tif readBytes == 0 {\n\t\t\t\t\tpart = strings.TrimLeft(part, \" \\t\")\n\t\t\t\t}\n\t\t\t\t// Ignore initial whitespace-only messages.\n\t\t\t\tif printed || strings.TrimSpace(part) != \"\" {\n\t\t\t\t\tprinted = true\n\t\t\t\t\tfmt.Fprint(output, part)\n\t\t\t\t}\n\t\t\t\tmessageReadBytes[msg.ID] = len(content)\n\t\t\t}\n\n\t\tcase <-ctx.Done():\n\t\t\tstopSpinner()","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/app/app.go#L398-L434","documentation":"RunNonInteractive streams partial message content to stdout, tracking how many bytes were already printed per message (messageReadBytes). When the final message content is shorter than the bytes already emitted — which would make content[readBytes:] panic — this error is returned instead. It indicates the message content changed/shrank between incremental updates, i.e. an internal consistency violation in message assembly.","triggerScenarios":"The tracked readBytes for a message ID exceeds len(msg.Content().String()) on the final assistant message — e.g. message parts were replaced/compacted, content was re-rendered shorter, or a provider returned truncated content after partial streaming.","commonSituations":"Provider streaming quirks where an interim message is later replaced by shorter final content; bugs after provider protocol changes; reasoning/thinking blocks being dropped from content between updates.","solutions":["Update crush to the latest version (streaming/assembly fixes land frequently)","Note the provider/model used; try a different model to see if it's provider-specific","File a bug with the provider name and SLOG_LEVEL=debug logs (message_length vs read_bytes)","As a workaround, retry the run; it is usually transient per-response"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := app.RunNonInteractive(ctx, w, prompt, \"\", \"\", true, \"\", false)\nif err != nil && strings.Contains(err.Error(), \"shorter than read bytes\") {\n\t// internal invariant violation: retry once, then report bug\n\tif rerr := app.RunNonInteractive(ctx, w, prompt, \"\", \"\", true, \"\", false); rerr != nil {\n\t\treturn fmt.Errorf(\"content mismatch persisted; report with debug logs: %w\", rerr)\n\t}\n\treturn nil\n}","preventionTips":["Keep crush updated — streaming assembly bugs are fixed upstream","Capture SLOG_LEVEL=debug logs when it happens (message_length vs read_bytes)","Note provider/model; test an alternate model to isolate provider-specific quirks"],"tags":["go","streaming","invariant","message-content"],"backgroundTag":"stream-content-mismatch","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}