Tencent/WeKnora · warning
failed to unmarshal FAQ import progress: %w
Error message
failed to unmarshal FAQ import progress: %w
What it means
GetFAQImportProgress wraps json.Unmarshal failures when deserializing the FAQImportProgress document stored in Redis. It means the stored bytes are not valid JSON for the current FAQImportProgress shape — typically stale data written by an older code version whose schema has since changed, or corrupted data.
Source
Thrown at internal/application/service/knowledge_faq_import.go:2776
func (s *knowledgeService) GetFAQImportProgress(ctx context.Context, taskID string) (*types.FAQImportProgress, error) {
if s.redisClient == nil {
if v, ok := s.memFAQProgress.Load(taskID); ok {
return v.(*types.FAQImportProgress), nil
}
return nil, werrors.NewNotFoundError("FAQ import task not found")
}
key := getFAQImportProgressKey(taskID)
data, err := s.redisClient.Get(ctx, key).Bytes()
if err != nil {
if errors.Is(err, redis.Nil) {
return nil, werrors.NewNotFoundError("FAQ import task not found")
}
return nil, fmt.Errorf("failed to get FAQ import progress from Redis: %w", err)
}
var progress types.FAQImportProgress
if err := json.Unmarshal(data, &progress); err != nil {
return nil, fmt.Errorf("failed to unmarshal FAQ import progress: %w", err)
}
// If task is completed, enrich with persisted result fields from database
if progress.Status == types.FAQImportStatusCompleted && progress.KnowledgeID != "" {
tenantID := ctx.Value(types.TenantIDContextKey).(uint64)
knowledge, err := s.repo.GetKnowledgeByID(ctx, tenantID, progress.KnowledgeID)
if err == nil && knowledge != nil {
if result, err := knowledge.GetLastFAQImportResult(); err == nil && result != nil {
progress.SuccessCount = result.SuccessCount
progress.FailedCount = result.FailedCount
progress.PartialFailedCount = result.PartialFailedCount
progress.SkippedCount = result.SkippedCount
progress.MergedCount = result.MergedCount
progress.AddedCount = result.AddedCount
progress.ImportMode = result.ImportMode
progress.ImportedAt = result.ImportedAt
progress.DisplayStatus = result.DisplayStatus
progress.ProcessingTime = result.ProcessingTimeView on GitHub (pinned to 988cbb0330)
Solutions
- DEL the stale progress key for the taskID and re-run the import
- Make unmarshaling tolerant: version the progress payload or ignore unknown fields (json.Decoder with DisallowUnknownFields removed)
- On unmarshal failure, fall back to NotFoundError so polling clients see a clean state
- Purge FAQ import progress keys after schema-changing deployments
Example fix
// before
var progress types.FAQImportProgress
if err := json.Unmarshal(data, &progress); err != nil {
return nil, fmt.Errorf("failed to unmarshal FAQ import progress: %w", err)
}
// after
var progress types.FAQImportProgress
if err := json.Unmarshal(data, &progress); err != nil {
logger.Errorf(ctx, "corrupt FAQ import progress for task %s, resetting: %v", taskID, err)
_ = s.redisClient.Del(ctx, key)
return nil, werrors.NewNotFoundError("FAQ import task not found")
} Defensive patterns
Strategy: type-guard
Validate before calling
// Reader-side sanity check before unmarshaling
data, err := redisClient.Get(ctx, key).Bytes()
if err == nil {
if !json.Valid(data) {
redisClient.Del(ctx, key) // drop corrupt/stale entry
return werrors.NewNotFoundError("FAQ import task not found")
}
} Type guard
func validFAQProgress(data []byte) (*types.FAQImportProgress, bool) {
var p types.FAQImportProgress
if err := json.Unmarshal(data, &p); err != nil || p.Status == "" {
return nil, false
}
return &p, true
} Try / catch
progress, err := svc.GetFAQImportProgress(ctx, taskID)
if err != nil {
if strings.Contains(err.Error(), "failed to unmarshal FAQ import progress") {
// stale schema from an older deployment: treat as no task
return nil, statusNotFound("FAQ import task not found (stale progress data)")
}
return nil, err
} Prevention
- Version the FAQImportProgress JSON (add a schemaVersion field) and migrate or discard old versions on deploy
- Purge FAQ import progress keys during schema-changing releases
- Keep FAQImportProgress fields backward-compatible (add, don't rename/retype)
- Treat unmarshal failures as stale data and fall back to NotFoundError rather than 500
When it happens
Trigger: A progress document written by a previous deployment (renamed/retyped fields, status strings, or KnowledgeID types) is read by current code; manual Redis edits; truncated/corrupt values from memory pressure or non-JSON writes.
Common situations: Deploying a new version that changed FAQImportProgress fields while old tasks are still in Redis; shared Redis across environments with different schema versions; someone debugging by writing raw values to the key.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- failed to get FAQ import progress from Redis: %w
- WEKNORA_REDIS_NAMESPACE must not contain braces
- WEKNORA_REDIS_NAMESPACE must not contain control characters
- args must be a string or an array of strings: %w
- logical operator %s requires an array of conditions
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/2ba3729749b57b5e.
Report an issue: GitHub.