{"record":{"id":"01fca6e49b67a763","repo":"gitbutlerapp/gitbutler","slug":"expected-project-id-to-be-either-a-projecthandle","errorCode":null,"errorMessage":"Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '{value}'","messagePattern":"Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-project-handle/src/project_handle.rs","lineNumber":157,"sourceCode":"    {\n        let value = <String as serde::Deserialize>::deserialize(deserializer)?;\n        value.parse().map_err(serde::de::Error::custom)\n    }\n}\n\nimpl std::str::FromStr for ProjectHandleOrLegacyProjectId {\n    type Err = anyhow::Error;\n\n    fn from_str(value: &str) -> Result<Self, Self::Err> {\n        if let Ok(handle) = value.parse::<ProjectHandle>() {\n            return Ok(Self::ProjectHandle(handle));\n        }\n        #[cfg(feature = \"legacy\")]\n        if let Ok(project_id) = value.parse::<LegacyProjectId>() {\n            return Ok(Self::LegacyProjectId(project_id));\n        }\n        #[cfg(feature = \"legacy\")]\n        return Err(anyhow::anyhow!(\n            \"Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '{value}'\"\n        ));\n        #[cfg(not(feature = \"legacy\"))]\n        return Err(anyhow::anyhow!(\n            \"Expected `project_id` to be a ProjectHandle, got '{value}'\"\n        ));\n    }\n}\n\nimpl<'de> serde::Deserialize<'de> for ProjectHandleOrLegacyProjectId {\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let value = <String as serde::Deserialize>::deserialize(deserializer)?;\n        value.parse().map_err(serde::de::Error::custom)\n    }\n}","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-project-handle/src/project_handle.rs#L139-L175","documentation":"ProjectHandleOrLegacyProjectId::from_str parses a project_id string into a ProjectHandle, and (when the 'legacy' feature is enabled) also accepts a legacy numeric LegacyProjectId. This error is thrown when the string is neither a valid ProjectHandle nor a valid legacy ProjectId, i.e. the whole parse chain failed.","triggerScenarios":"Deserializing or parsing a project_id with a string that is neither a valid handle nor a numeric legacy id — e.g. an empty string, a malformed handle, or a non-numeric legacy id like 'abc123' when feature \"legacy\" is compiled in.","commonSituations":"Users passing a human-readable project name containing invalid characters, stale IDs from an older data format, or API callers sending arbitrary strings as project_id.","solutions":["Verify the string is a valid ProjectHandle (correct format/characters)","If using legacy numeric IDs, ensure the value is numeric and the 'legacy' feature is enabled","Confirm the project exists; obtain a fresh project_id via the project listing API"],"exampleFix":"// before\nlet id = input.parse::<ProjectHandleOrLegacyProjectId>()?; // input = \"my project\"\n// after\nlet id = ProjectHandle::new(\"my-project\")?.parse::<ProjectHandleOrLegacyProjectId>()?;","handlingStrategy":"validation","validationCode":"fn parse_project_id(raw: &str) -> anyhow::Result<ProjectHandleOrLegacyProjectId> {\n    anyhow::ensure!(!raw.trim().is_empty(), \"project_id must not be empty\");\n    raw.parse::<ProjectHandleOrLegacyProjectId>().map_err(Into::into)\n}","typeGuard":"fn is_legacy_id(s: &str) -> bool { s.parse::<u64>().is_ok() }","tryCatchPattern":"match raw.parse::<ProjectHandleOrLegacyProjectId>() {\n    Ok(id) => id,\n    Err(e) => return Err(anyhow::anyhow!(\"bad project_id '{raw}': {e}\")),\n}","preventionTips":["Always obtain project ids from the project listing API rather than free text","Validate handle format (allowed characters) before parsing","Document that legacy numeric ids require the 'legacy' feature"],"tags":["parsing","project-id","invalid-input"],"backgroundTag":"invalid-argument-format","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}