Tencent/WeKnora · critical

panic during FAQ import: %v

Error message

panic during FAQ import: %v

What it means

executeFAQImport wraps its body in a deferred recover(); if any code in the import panics (nil map write, nil pointer dereference, index out of range), the panic is converted into this error and assigned to the task result. It indicates a programming defect in the import path rather than an expected failure. The stack trace is logged with the task ID.

Source

Thrown at internal/application/service/knowledge_faq_import.go:1369

// executeFAQImport 执行实际的FAQ导入逻辑
func (s *knowledgeService) executeFAQImport(ctx context.Context, taskID string, kbID string,
	payload *types.FAQBatchUpsertPayload, tenantID uint64, processedCount int,
	progress *types.FAQImportProgress,
) (err error) {
	// 保存知识库和embedding模型信息,用于清理索引
	var kb *types.KnowledgeBase
	var embeddingModel embedding.Embedder
	totalEntries := len(payload.Entries) + processedCount

	// Recovery机制:如果发生任何错误或panic,回滚所有已创建的chunks和索引数据
	defer func() {
		// 捕获panic
		if r := recover(); r != nil {
			buf := make([]byte, 8192)
			n := runtime.Stack(buf, false)
			stack := string(buf[:n])
			logger.Errorf(ctx, "FAQ import task %s panicked: %v\n%s", taskID, r, stack)
			err = fmt.Errorf("panic during FAQ import: %v", r)
		}
	}()

	kb, err = s.validateFAQKnowledgeBase(ctx, kbID)
	if err != nil {
		return err
	}

	kb.EnsureDefaults()

	// 获取embedding模型,用于后续清理索引
	embeddingModel, err = s.modelService.GetEmbeddingModel(ctx, kb.EmbeddingModelID)
	if err != nil {
		return fmt.Errorf("failed to get embedding model: %w", err)
	}
	faqKnowledge, err := s.ensureFAQKnowledge(ctx, tenantID, kb)
	if err != nil {
		return err

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Find the panic value and stack trace in the logs (logged as 'FAQ import task %s panicked')
  2. Fix the nil/nil assumptions or malformed metadata handling at the panic site
  3. Add nil guards before calling FAQMetadata()/map lookups on chunk data
  4. Add unit tests with legacy/malformed chunk records to cover the panic path

Example fix

// before
for _, chunk := range existingChunks {
	meta, cErr := chunk.FAQMetadata()
	if cErr == nil {
		q := meta.StandardQuestion
		stdQToChunk[q] = chunk
	}
}
// after
for _, chunk := range existingChunks {
	if chunk == nil {
		continue
	}
	meta, cErr := chunk.FAQMetadata()
	if cErr != nil || meta == nil || meta.StandardQuestion == "" {
		continue
	}
	stdQToChunk[meta.StandardQuestion] = chunk
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate payload entries before execution to avoid panic-prone paths
for i, e := range payload.Entries {
	if e.StandardQuestion == "" {
		return fmt.Errorf("entry %d: empty standard question", i)
	}
}

Type guard

func (c *types.Chunk) HasValidFAQMetadata() bool {
	if c == nil {
		return false
	}
	meta, err := c.FAQMetadata()
	return err == nil && meta != nil && meta.StandardQuestion != ""
}

Try / catch

err := ProcessFAQImport(ctx, taskID, kbID, payload)
if err != nil && strings.HasPrefix(err.Error(), "panic during FAQ import") {
	logger.Errorf(ctx, "import panicked; task %s marked failed, report bug with stack trace", taskID)
	markTaskFailed(taskID, err)
	return err // do not auto-retry panics; fix code first
}
return err

Prevention

When it happens

Trigger: Any panic inside executeFAQImport: e.g., chunk.FAQMetadata() returning unexpected data leading to nil dereference, malformed FAQMetadata map access, or a callee panicking on malformed payload entries.

Common situations: New FAQ metadata format or older chunk records lacking fields the code assumes; concurrent map write; regression introduced by a recent code change; unexpected nil from a repository returning (nil, nil).

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/b9d5bb37db6d3e01. Report an issue: GitHub.