instructure/canvas-lms · warning

Document index status sync failed for ai_experience #

Error message

Document index status sync failed for ai_experience #{ai_experience.id}: #{e.message}

What it means

In AiExperiences::ConversationContextDocumentsService#sync_index_status, a LlmConversation::Errors::ConversationError raised while syncing document index status is caught and only logged as a warning; no exception propagates. The log line 'Document index status sync failed ...' means the sync against the LLM conversation service failed (service error), so the AI experience's document index status may be stale. It is a swallowed, non-fatal sync failure, not a crash.

Solutions

  1. Check the Rails logs for the full warn message and the embedded e.message to identify the underlying LlmConversation::Errors::ConversationError cause.
  2. Verify the ai_experience's llm_conversation_context exists and is valid (llm_conversation_context_id not nil/dangling).
  3. Confirm the LLM conversation backend service is reachable and credentials/config are correct in the environment.
  4. Retry the sync once the upstream issue is resolved, or re-trigger indexing via trigger_indexing if status remains stale.

Example fix

// before: error swallowed, status silently stale
rescue LlmConversation::Errors::ConversationError => e
  Rails.logger.warn("Document index status sync failed for ai_experience #{ai_experience.id}: #{e.message}")
end

// after: track failure in return value so callers can react
rescue LlmConversation::Errors::ConversationError => e
  Rails.logger.warn("Document index status sync failed for ai_experience #{ai_experience.id}: #{e.message}")
  { status: :sync_failed, failed_file_names: [] }
end
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling sync_index_status
if ai_experience.llm_conversation_context_id.blank?
  Rails.logger.warn("Skipping index status sync: no llm_conversation_context for ai_experience #{ai_experience.id}")
else
  service.sync_index_status(ai_experience)
end

Type guard

def syncable?(ai_experience)
  ai_experience.respond_to?(:llm_conversation_context_id) && ai_experience.llm_conversation_context_id.present?
end

Try / catch

begin
  result = service.sync_index_status(ai_experience)
rescue LlmConversation::Errors::ConversationError => e
  Rails.logger.warn("sync_index_status failed for #{ai_experience.id}: #{e.message}")
  # schedule retry / mark status stale
end

Prevention

When it happens

Trigger: Calling sync_index_status(ai_experience) when the associated llm_conversation_context or its remote LLM conversation service raises LlmConversation::Errors::ConversationError — e.g. context missing/invalid, remote API rejected the request, or conversation state is inconsistent while computing new_status/failed_file_names.

Common situations: Background/inline sync of AI Experience context document indexing status; LLM conversation backend downtime or 4xx/5xx responses; an ai_experience whose llm_conversation_context_id points to a deleted or unreachable context; transient network failures during status polling.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/b2f43ca1f79e66d9. Report an issue: GitHub.

Appendix: source

Thrown at app/services/ai_experiences/conversation_context_documents_service.rb:59

                   else
                     "in_progress"
                   end

      ai_experience.update_column(:context_index_status, new_status)

      failed_file_names = if new_status == "failed"
                            failed_doc_ids = documents.select { |d| d["status"] == "failed" }.pluck("id")
                            ai_experience.ai_experience_context_files
                                         .where(llm_conversation_service_document_id: failed_doc_ids)
                                         .preload(:attachment)
                                         .filter_map { |cf| cf.attachment&.display_name }
                          else
                            []
                          end

      { status: new_status, failed_file_names: }
    rescue LlmConversation::Errors::ConversationError => e
      Rails.logger.warn("Document index status sync failed for ai_experience #{ai_experience.id}: #{e.message}")
    end

    def trigger_indexing(ai_experience:, context_file_ids: nil)
      context_id = ai_experience.llm_conversation_context_id
      return unless context_id.present?

      scope = ai_experience.ai_experience_context_files
      scope = scope.where(id: context_file_ids) if context_file_ids.present?
      context_file_records = scope.preload(:attachment)
      return if context_file_records.empty?

      context_file_records.each do |context_file|
        file = context_file.attachment
        next if file.nil? || file.file_state == "deleted"

        response = @client.post(
          "/contexts/#{context_id}/documents",
          payload: { url: file.public_url, sourceType: "file" }

View on GitHub (pinned to 1c9f0bb801)