{"record":{"id":"64ebf84644d7529a","repo":"zeroclaw-labs/zeroclaw","slug":"invalid-project-key-key-expected-ascii-alphan","errorCode":null,"errorMessage":"Invalid project key '{key}'. Expected ASCII alphanumeric, e.g. PROJ","messagePattern":"Invalid project key '(.+?)'\\. Expected ASCII alphanumeric, e\\.g\\. PROJ","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/jira_tool.rs","lineNumber":1328,"sourceCode":"    });\n    if valid {\n        Ok(())\n    } else {\n        anyhow::bail!(\n            \"Invalid issue key '{key}'. Expected format: PROJECT-123 (e.g. PROJ-42, proj-42)\"\n        )\n    }\n}\n\n/// Validates that `key` matches the Jira project key format. Same character\n/// class as the project portion of `validate_issue_key` so the two stay in\n/// step.\nfn validate_project_key(key: &str) -> anyhow::Result<()> {\n    let valid = !key.is_empty() && key.chars().all(|c| c.is_ascii_alphanumeric());\n    if valid {\n        Ok(())\n    } else {\n        anyhow::bail!(\"Invalid project key '{key}'. Expected ASCII alphanumeric, e.g. PROJ\")\n    }\n}\n\n// ── Response shaping ──────────────────────────────────────────────────────────\n\n/// Safely extracts the first 10 characters (date prefix) from a string.\n/// Returns the full string if it is shorter than 10 characters instead of\n/// panicking on out-of-bounds slice indexing.\nfn date_prefix(s: &str) -> &str {\n    s.get(..10).unwrap_or(s)\n}\n\nfn shape_basic(raw: &Value) -> Value {\n    let f = &raw[\"fields\"];\n    let rf = &raw[\"renderedFields\"];\n\n    // Build a lookup map from comment ID → rendered body for O(1) access\n    // instead of scanning the rendered array for each comment (O(n²)).","sourceCodeStart":1310,"sourceCodeEnd":1346,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/jira_tool.rs#L1310-L1346","documentation":"Local format validation of a project key used by create_ticket: the key must be non-empty and every character ASCII alphanumeric. Notably it rejects '-' and other punctuation, so passing a full issue key (\"PROJ-42\") here fails. It runs before the summary/issue_type checks and before any network call.","triggerScenarios":"Calling create_ticket with an issue key instead of a project key (\"PROJ-42\"); an empty project string from a missing config value; project keys with punctuation that some legacy Jira instances allow; whitespace in the key from untrimmed config.","commonSituations":"Config files storing the default project as blank; agents conflating project key and issue key; migrating from instances whose project keys contain characters Jira no longer permits.","solutions":["Pass only the project key, e.g. \"PROJ\", not an issue key.","Make the project key a required, trimmed configuration value and fail fast at startup if it is blank.","Validate the format in your own layer before calling create_ticket (see below)."],"exampleFix":"// before\njira.create_ticket(\"PROJ-42\", \"Bug\", \"Crash on save\", None, None, None, None).await?; // -> error: issue key passed as project\n\n// after\njira.create_ticket(\"PROJ\", \"Bug\", \"Crash on save\", None, None, None, None).await?;","handlingStrategy":"validation","validationCode":"fn is_valid_project_key(key: &str) -> bool {\n    !key.is_empty() && key.chars().all(|c| c.is_ascii_alphanumeric())\n}\nif !is_valid_project_key(project.trim()) { anyhow::bail!(\"bad project key\"); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fail fast at startup if the configured default project key is blank.","Strip anything after '-' before passing (guards against issue/project key confusion).","Keep project keys in typed config, not free-form user input."],"tags":["jira","validation","project-key","format"],"backgroundTag":"invalid-identifier-format","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}