{"record":{"id":"49c77a6e04c36dd0","repo":"gitbutlerapp/gitbutler","slug":"expected-project-id-to-be-a-projecthandle-got","errorCode":null,"errorMessage":"Expected `project_id` to be a ProjectHandle, got '{value}'","messagePattern":"Expected `project_id` to be a ProjectHandle, got '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-project-handle/src/project_handle.rs","lineNumber":161,"sourceCode":"}\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}\n\nimpl serde::Serialize for ProjectHandleOrLegacyProjectId {\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-project-handle/src/project_handle.rs#L143-L179","documentation":"This is the non-legacy variant of the ProjectHandleOrLegacyProjectId parse failure. When the crate is built without the 'legacy' feature, only a ProjectHandle is accepted; this error is thrown for any string that fails ProjectHandle parsing. The legacy ProjectId path does not exist in this build.","triggerScenarios":"Parsing/deserializing a project_id (e.g. from JSON) that is not a valid ProjectHandle while the crate is compiled with default features (no 'legacy'), such as a bare numeric id like '42'.","commonSituations":"Clients still sending legacy numeric project IDs after a migration to handles; payloads serialized by an older client version against a newer server.","solutions":["Send a valid ProjectHandle string instead of a legacy numeric ProjectId","Rebuild/reconfigure with the 'legacy' feature enabled if legacy IDs must be accepted","Update callers to the current API that returns project handles"],"exampleFix":"// before\nlet id = \"42\".parse::<ProjectHandleOrLegacyProjectId>()?;\n// after\nlet id = \"my-project-handle\".parse::<ProjectHandleOrLegacyProjectId>()?;","handlingStrategy":"validation","validationCode":"fn ensure_project_handle(raw: &str) -> anyhow::Result<ProjectHandleOrLegacyProjectId> {\n    anyhow::ensure!(raw.parse::<u64>().is_err(), \"legacy numeric project ids are not supported; use a project handle\");\n    raw.parse::<ProjectHandleOrLegacyProjectId>().map_err(Into::into)\n}","typeGuard":null,"tryCatchPattern":"let id = raw.parse::<ProjectHandleOrLegacyProjectId>()\n    .with_context(|| format!(\"expected project handle, got '{raw}'\"))?;","preventionTips":["Migrate all clients to ProjectHandle strings; stop emitting legacy numeric ids","Confirm feature flags ('legacy') match between build and data format","Validate ids at deserialization boundaries with helpful context"],"tags":["parsing","project-id","migration"],"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"}