zeroclaw-labs/zeroclaw · error

tag '{}' contains a comma, which is used as the tag separato

Error message

tag '{}' contains a comma, which is used as the tag separator

What it means

KnowledgeGraph stores a node's tags as a single comma-joined TEXT column (tags.join(",") at insert; split back on ',') and mirrors that string into the FTS index, so the comma is the reserved separator. add_node rejects any tag containing a comma before writing, keeping stored tags unambiguously splittable; without this check a tag like "rust, memory" would silently become two tags on read-back.

Source

Thrown at crates/zeroclaw-memory/src/knowledge_graph.rs:224

        tags: &[String],
        source_project: Option<&str>,
    ) -> anyhow::Result<String> {
        let conn = self.conn.lock();

        // Enforce max_nodes limit.
        let count: usize = conn.query_row("SELECT COUNT(*) FROM nodes", [], |r| r.get(0))?;
        if count >= self.max_nodes {
            anyhow::bail!(
                "knowledge graph node limit reached ({}/{})",
                count,
                self.max_nodes
            );
        }

        // Reject tags containing commas since comma is the separator in storage.
        for tag in tags {
            if tag.contains(',') {
                anyhow::bail!(
                    "tag '{}' contains a comma, which is used as the tag separator",
                    tag
                );
            }
        }

        let id = Uuid::new_v4().to_string();
        let now = Utc::now().to_rfc3339();
        let tags_str = tags.join(",");

        conn.execute(
            "INSERT INTO nodes (id, node_type, title, content, tags, created_at, updated_at, source_project)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
            params![
                id,
                node_type.as_str(),
                title,
                content,

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Split comma-separated input into separate tags before calling add_node: "a, b".split(',').map(str::trim).filter(|s| !s.is_empty()).
  2. If a comma is genuinely part of the tag's meaning, replace it with another separator (e.g. '-' or '_') since the storage format cannot encode it.
  3. Validate/sanitize tags at the boundary where user or LLM text enters the system, not at the graph call site only.
  4. Keep the failing tag from the message — it names exactly which element to fix.

Example fix

// before
let tags = vec!["rust, memory".to_string()];
graph.add_node(NodeType::Pattern, "t", "c", &tags, None)?; // tag contains a comma

// after
let tags: Vec<String> = "rust, memory"
    .split(',')
    .map(str::trim)
    .filter(|s| !s.is_empty())
    .map(str::to_string)
    .collect(); // ["rust", "memory"]
graph.add_node(NodeType::Pattern, "t", "c", &tags, None)?;
Defensive patterns

Strategy: validation

Validate before calling

// Sanitize tags at the boundary
fn clean_tags(raw: &str) -> Vec<String> {
    raw.split(',')
        .map(str::trim)
        .filter(|s| !s.is_empty() && !s.contains(','))
        .map(str::to_string)
        .collect()
}
let tags = clean_tags(&user_tags);
assert!(tags.iter().all(|t| !t.contains(',')));
graph.add_node(NodeType::Lesson, title, content, &tags, None)?;

Type guard

pub fn tags_are_graph_safe(tags: &[String]) -> bool {
    tags.iter().all(|t| !t.is_empty() && !t.contains(','))
}

Try / catch

// Repair per-tag instead of failing the whole capture
for tag in tags.iter().filter(|t| t.contains(',')) {
    tracing::warn!(tag, "tag contains reserved separator ','; splitting");
}
let tags: Vec<String> = tags.iter().flat_map(|t| t.split(',')).map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect();
graph.add_node(node_type, title, content, &tags, None)?;

Prevention

When it happens

Trigger: Calling add_node (or a capture path feeding it) with a tags slice where any element contains ',', e.g. tags = &["rust, memory".to_string()] or user-supplied tag strings pasted from a comma-separated list without splitting.

Common situations: Accepting user-provided tags verbatim ("tags: a, b, c" typed as one string); forwarding CSV data or LLM-extracted tag lists straight into add_node; test fixtures that join tags themselves before passing them.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/732daea93af8c90c. Report an issue: GitHub.