ErrLookup › Background articles › "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them
"invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them
"Invalid id", "invalid identifier", and "invalid block ID" errors are format-validation failures: a library rejects an identifier because its shape doesn't match the expected pattern — wrong prefix, wrong length, illegal characters, or path-unsafe input — before it ever searches for the object. Developers hit them when hand-crafting IDs, pasting from URLs or markdown, or passing the wrong ID type entirely. This article explains why so many libraries validate ID format up front, what the common pattern violations look like across Go, Ruby, Rust, TypeScript, PHP, and Java projects, and how to fix and prevent them.
Distilled from 103 documented records across 37 repositories.
Background
This family covers a guard that appears at almost every layer of modern software: a function receives an identifier, checks it against a pattern — a regex, a length-and-charset rule, a prefix convention — and refuses to proceed when the shape is wrong. Crucially, the rejection happens before any lookup. SiYuan's model.ErrInvalidID is explicit about this distinction: a malformed attribute-view or block ID never reaches the blocktree, so the error means 'bad format', not 'not found' (match it with errors.Is to tell the two apart). Ubicloud's SDK validates inference API keys against \Aak[a-tv-z0-9]{24}\z client-side before any request, and Hmbown/CodeWhale's preallocated task-id check (task_ + 16 hex chars) exists because a collision would silently overwrite persisted records. These are deliberate fail-fast designs: a wrong-shaped ID can never exist server-side, so attempting the operation would be pointless at best and dangerous at worst.
The dangerous part of the family is path and URL safety. Several validations exist not for correctness but because the identifier gets interpolated into a filesystem path or a REST URL. Ubicloud rejects SSH public key strings containing '/' because the value is placed into the path ssh-public-key/<value> — a slash would corrupt or traverse the URL. Mastra's sandbox worker restricts executionId to letters, digits, dots, underscores, and hyphens because the id is used to build filesystem paths and shell arguments. openhuman's draft-id rule (non-empty, ≤64 chars, ASCII alphanumerics, '-' and '_' only) exists purely because an unvalidated path join would let '../' escape the drafts directory. In these cases the 'invalid id' error is a security boundary, and libraries intentionally accept only a narrow allowlist rather than trying to blacklist traversal sequences.
From the caller's side, these errors usually mean the ID never came from the system that mints IDs. The recurring advice across records is to source identifiers from the system's own listing or lookup endpoints — SiYuan's lsNotebooks and getBlockKramdownID, Ubicloud's ai api-key list, Semantic Scholar's search — instead of constructing them. Typical bad inputs are predictable: a document path or notebook folder name where an ID belongs, a raw numeric ID where a PHID is required (Phabricator's PHID-PCD- column check), a full resource URL pasted where a bare id was expected, an ID with whitespace or zero-width characters from copy-paste, or a mismatched prefix such as passing an sk- SSH key id where an ak- inference key is expected.
The exact rules are library-specific and sometimes even inconsistent within one project. Ubicloud's SDK excludes 'u' from its ID alphabet ([a-tv-z0-9]) while its CLI checks the looser [a-z0-9]{24}, so an id containing 'u' can pass the CLI yet fail the SDK. SiYuan's block IDs are 14 digits plus 7 lowercase alphanumeric characters (22 total) while its session ids are exactly 20 chars of [0-9a-z]; its update APIs reject the whole batch for one bad ID with no element index in the message. Claude-mem's mode ids enforce kebab-case with at most one '--' inheritance separator. The pattern to internalize is the mechanism, not any single regex: validate the shape, include the offending value in the message, and reject before lookup, disk, or network.
Common causes
- Hand-crafted or self-invented IDs. Passing a constructed string — a slug, a title, a UUID, a document path, a folder name — where the library expects an ID in its own minted format. SiYuan rejects anything that isn't its timestamp-prefixed block ID; CodeWhale rejects task ids that aren't task_ + 16 hex. IDs should come from the system's own list/get endpoints.
- Wrong ID type or prefix. Passing a related but different identifier: an SSH key id (sk...) where an inference API key (ak...) is expected, a numeric column ID instead of a PHID-PCD- column PHID in Phabricator, a bare project key instead of PROJECT-123 in zeroclaw, or a Jira issue number instead of its key. Check the prefix convention before retrying.
- URLs and path fragments passed as IDs. Pasting a full resource URL, registry path, or route prefix where a bare id belongs — 'inference-api-key/ak...', a full browse URL for a Jira key, an arxiv.org/abs/ link instead of the bare arXiv id. Some libraries reject '/' outright because the value is interpolated into a REST path.
- Copy-paste artifacts and whitespace. Trailing whitespace or newlines from files and env vars, zero-width spaces glued on from kramdown text, quotes and trailing punctuation from markdown IAL syntax, or URL-encoded values like '..%2Fx'. Trim and sanitize before validation.
- Illegal characters for the target format. Uppercase letters where lowercase is required, dots, spaces, slashes, '@', or leading/trailing hyphens. Kamal service names allow only [a-z0-9_-]; litellm key aliases must start and end alphanumeric; claude-mem mode ids must be kebab-case. Each format has its own allowlist.
- Wrong length or truncated ID. Truncated 20-char SiYuan block IDs (22 required), ids too short for the 24-char Ubicloud pattern, or over-long values past a 64-char cap. CodeWhale widened its task id from 8 to 16 hex, so stale generators produce ids of the wrong length.
- Path-traversal-shaped input. Values containing '..', leading dots, or '/' such as '../../config' or '.hidden', rejected by openhuman's draft-id guard and block/buzz's repo identifier check. These validations are security boundaries and intentionally strict.
- Unvalidated template or variable interpolation. Identifiers assembled from undefined variables or unfilled template slots producing garbage like 'undefined/' or '{{name}}' resolving to a value with spaces. Log the assembled identifier before use; validate at the point of entry, not deep in the pipeline.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Go deeper
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Documented occurrences
- invalid block ID [%s] (siyuan-note/siyuan)
- invalid #{self.class.fragment} id (ubicloud/ubicloud)
- Invalid mode ID: ${modeId} (thedotmack/claude-mem)
- Invalid issue key '{key}'. Expected format: PROJECT-123 (e.g. PROJ-42, proj-42) (zeroclaw-labs/zeroclaw)
- invalid SSH public key id format (ubicloud/ubicloud)
- no inference API key with id #{id} exists (ubicloud/ubicloud)
- invalid id (siyuan-note/siyuan)
- caveman agent: invalid agent id ${JSON.stringify(options.id)} (JuliusBrussee/caveman)
- [{0}] = [{1}] must be "{2}" (apache/hadoop)
- invalid box ID [%s] (siyuan-note/siyuan)
- When moving objects between columns on a board, columns must be identified by PHIDs. This transaction uses "%s" to identify a column, but that is not a valid column PHID. (phacility/phabricator)
- Invalid key_alias format. Must be 2-255 characters, start/end with alphanumeric, and only contain a-zA-Z0-9_-/.@. (BerriAI/litellm)
- invalid session id (siyuan-note/siyuan)
- found invalid ID [%s] (siyuan-note/siyuan)
- nuget package id "${value}" is not a valid NuGet identifier (jackwener/OpenCLI)
- Invalid component identifier: ${raw}. Expected author/slug. (jackwener/OpenCLI)
- Expected `project_id` to be either a ProjectHandle or a legacy ProjectId, got '{value}' (gitbutlerapp/gitbutler)
- Service name can only include alphanumeric characters, hyphens, and underscores (basecamp/kamal)
- semanticscholar paper id "${value}" is not recognised (jackwener/OpenCLI)
- invalid childRef '%s': must be alphanumeric, dash, underscore, or dot only (gastownhall/beads)
…and 83 more across the corpus — use search.
Honest provenance: generated on 2026-08-31 from AI-assisted analysis of the linked records. See how records are made.