Tencent/WeKnora · error
invalid task ID format: %s
Error message
invalid task ID format: %s
What it means
ParseTaskID splits a task ID on '_' and requires at least 4 segments: task type parts, tenant ID, timestamp, and UUID. If fewer than 4 underscore-separated parts exist the ID cannot contain the mandatory tenant_timestamp pair and 'invalid task ID format' is returned. Task types may themselves contain underscores, so the parser scans for the tenant/timestamp pair rather than trusting the first segment.
Source
Thrown at internal/utils/taskid.go:73
}
if len(businessID) > 0 && businessID[0] != "" {
components = append(components, sanitizeBusinessID(businessID[0]))
}
return strings.Join(components, "_")
}
// ParseTaskID parses a task ID generated by GenerateTaskID and returns its components.
// Returns taskType, tenantID, timestamp, uuid, businessID, and error.
//
// Task types may contain underscores (e.g. "faq_import", "kb_clone"), so the
// parser locates the tenant/timestamp pair rather than assuming parts[0] is
// the full task type.
func ParseTaskID(taskID string) (taskType string, tenantID uint64, timestamp int64, uuidPart string, businessID string, err error) {
parts := strings.Split(taskID, "_")
if len(parts) < 4 {
err = fmt.Errorf("invalid task ID format: %s", taskID)
return
}
tenantIdx := -1
for i := 1; i < len(parts)-2; i++ {
candidateTenant, parseErr := strconv.ParseUint(parts[i], 10, 64)
if parseErr != nil || candidateTenant == 0 {
continue
}
candidateTS, parseErr := strconv.ParseInt(parts[i+1], 10, 64)
if parseErr != nil || candidateTS < 1_000_000_000_000 {
continue
}
tenantID = candidateTenant
timestamp = candidateTS
tenantIdx = i
break
}View on GitHub (pinned to 988cbb0330)
Solutions
- Regenerate the task ID with the correct format: <taskType>_<tenantID>_<timestamp>_<uuid> (e.g. faq_import_42_1700000000_ab12cd34)
- Log the full raw taskID value and count its '_' segments to confirm the corruption point
- Check the producer that created the ID for missing tenant/timestamp/uuid fields
- Add validation at the producer side so malformed IDs are never enqueued
Example fix
// before taskID := "kb_clone_42" // only 3 segments // after taskID := "kb_clone_42_1700000000_9f8e7d6c" // taskType_tenant_ts_uuid
Defensive patterns
Strategy: validation
Validate before calling
parts := strings.Split(taskID, "_")
if len(parts) < 4 {
return fmt.Errorf("task ID %q too short: need <type>_<tenant>_<ts>_<uuid>", taskID)
} Type guard
func looksLikeTaskID(id string) bool {
parts := strings.Split(id, "_")
if len(parts) < 4 { return false }
for i := 1; i < len(parts)-2; i++ {
tenant, e1 := strconv.ParseUint(parts[i], 10, 64)
_, e2 := strconv.ParseInt(parts[i+1], 10, 64)
if e1 == nil && e2 == nil && tenant > 0 { return true }
}
return false
} Try / catch
taskType, tenantID, ts, uuid, biz, err := utils.ParseTaskID(raw)
if err != nil {
log.Printf("unparseable task ID %q: %v", raw, err)
return fmt.Errorf("corrupt task ID from queue: %w", err)
} Prevention
- Never truncate task IDs (DB column limits, log copy-paste)
- Generate IDs only through the producer helper that appends tenant/timestamp/uuid
- Version task-ID formats if the legacy scheme lacked segments
- Validate IDs at queue consumption before processing
When it happens
Trigger: ParseTaskID (via TaskTenantID or tests) receives a task ID with fewer than 4 '_'-separated segments — e.g. 'import_123', 'faqs', an empty string, or a truncated/corrupted ID read from a queue message or DB row.
Common situations: Legacy task IDs generated by an older ID format, IDs truncated by column limits or log copy-paste, hand-written test fixtures, or a producer bug that skipped appending tenant/timestamp/uuid segments.
Related errors
- parse feed %s: %w
- invite code has expired
- join request not found
- failed to retrieve: %s
- opensearch: index not found
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/d571ee07e3a2f3cc.
Report an issue: GitHub.