{"record":{"id":"59facf8f74a9d16d","repo":"jackwener/OpenCLI","slug":"crates-crate-name-value-is-not-a-valid-crates","errorCode":null,"errorMessage":"crates crate name \"${value}\" is not a valid crates.io name","messagePattern":"crates crate name \"(.+?)\" is not a valid crates\\.io name","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/crates/utils.js","lineNumber":20,"sourceCode":"import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nexport const CRATES_BASE = 'https://crates.io';\nconst UA = 'opencli-crates-adapter (+https://github.com/jackwener/opencli)';\n\n// crates.io crate names: 1-64 chars, ascii letters/digits/-_, must start with a letter.\nconst CRATE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;\n\nexport function requireString(value, label) {\n    const s = String(value ?? '').trim();\n    if (!s) throw new ArgumentError(`crates ${label} cannot be empty`);\n    return s;\n}\n\nexport function requireCrateName(value) {\n    const s = String(value ?? '').trim();\n    if (!s) throw new ArgumentError('crates crate name is required (e.g. \"serde\", \"tokio\")');\n    if (!CRATE_NAME.test(s)) {\n        throw new ArgumentError(\n            `crates crate name \"${value}\" is not a valid crates.io name`,\n            'Names start with an ASCII letter, then 0-63 chars of letters / digits / \"_-\".',\n        );\n    }\n    return s;\n}\n\nexport function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {\n    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`crates ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`crates ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/crates/utils.js#L2-L38","documentation":"requireCrateName validates crate names against the crates.io naming rule: start with an ASCII letter, then 0-63 chars of letters/digits/underscore/hyphen (regex /^[A-Za-z][A-Za-z0-9_-]{0,63}$/). Values failing this pattern throw this ArgumentError with a hint about the allowed format.","triggerScenarios":"Passing a name that starts with a digit or symbol ('2fa'), contains illegal characters ('serde json', 'serde/json', 'serde@1'), exceeds 64 chars, is a full URL ('https://crates.io/crates/serde'), or includes a version suffix ('serde@1.0.0').","commonSituations":"Pasting a crates.io URL as the name, appending @version like in npm/cargo CLI habits, names with spaces from copy/paste, or internal package names that were never valid on crates.io.","solutions":["Use only the bare crate name: letters/digits/_/- starting with a letter, max 64 chars.","Strip URL prefixes and version suffixes: extract the last path segment and remove any '@...' part.","Look the name up with `crates search` if unsure of the exact spelling.","Pre-validate with the same regex before calling the API."],"exampleFix":"// before\nawait cli.crates.crate({ name: 'https://crates.io/crates/serde' });\n// after\nawait cli.crates.crate({ name: 'serde' });","handlingStrategy":"validation","validationCode":"const CRATE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;\nfunction sanitizeCrateName(input) {\n  const s = String(input ?? '').trim();\n  const bare = s.split('/').pop()?.split('@')[0] ?? ''; // strip URL path and @version\n  if (!CRATE_NAME.test(bare)) throw new Error(`invalid crates.io name: ${input}`);\n  return bare;\n}","typeGuard":"function isValidCrateName(v) {\n  return typeof v === 'string' && /^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(v);\n}","tryCatchPattern":"try {\n  await cli.crates.crate({ name });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('not a valid crates.io name')) {\n    console.error('Names: ASCII letter first, then letters/digits/_/- (max 64 chars). No URLs or @versions.');\n  } else throw e;\n}","preventionTips":["Strip URL prefixes and @version suffixes before passing a name.","Reject names starting with digits/symbols early in your own input parsing.","Copy bare names, not full crates.io URLs.","Reuse the same regex the adapter uses for consistency."],"tags":["argument-validation","naming","crates-io"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}