lfnovo/open-notebook · error · ValueError
Invalid {kind} name: {value!r}
Error message
Invalid {kind} name: {value!r} What it means
_ensure_safe_identifier() validates table/relationship names interpolated into SurrealQL and rejects anything not matching ^[a-zA-Z_][a-zA-Z0-9_]*$. It is a SQL/SurrealQL injection guard: repo_relate/repo_upsert/repo_update build query strings from these names, so unsafe values (spaces, punctuation, digits first, non-strings, f-string built names) are refused before reaching the database.
Source
Thrown at open_notebook/database/repository.py:31
# Keep the internal SurrealDB websocket out of any configured HTTP proxy
# (issue #1160). Runs at import time - i.e. before any db_connection() can be
# opened - so it protects every entrypoint (API + worker) that touches the DB.
ensure_internal_no_proxy()
T = TypeVar("T", Dict[str, Any], List[Dict[str, Any]])
# Bare SurrealDB table/relation identifier: no ':', whitespace, or query
# syntax. Used to validate the parts of RELATE/UPSERT/UPDATE that name a
# table or edge-relation and therefore can't be bound as a query parameter
# (SurrealQL only allows binding record/table *values*, not identifiers in
# that position).
_IDENTIFIER_RE = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$")
def _ensure_safe_identifier(value: str, kind: str) -> str:
"""Validate a table/relationship name before it is interpolated into a query."""
if not isinstance(value, str) or not _IDENTIFIER_RE.match(value):
raise ValueError(f"Invalid {kind} name: {value!r}")
return value
def _get_env_or_default(name: str, default: str) -> str:
value = os.getenv(name)
return value if value else default
def get_database_url() -> str:
"""Get database URL with backward compatibility"""
surreal_url = os.getenv("SURREAL_URL")
if surreal_url:
return surreal_url
# Fallback to old format - WebSocket URL format
address = os.getenv("SURREAL_ADDRESS", "localhost")
port = os.getenv("SURREAL_PORT", "8000")
return f"ws://{address}/rpc:{port}"View on GitHub (pinned to a7de90d38a)
Solutions
- Fix the caller to pass a plain identifier: letters, digits, underscores, not starting with a digit
- If the name comes from user input, whitelist it against known tables/relationships before calling the repo function
- Check for accidental RecordID/None being passed where a string name is expected
- If you truly need exotic table names, rename the table to a safe identifier at the schema level
Example fix
// before await repo_relate(src, "linked-to note", tgt) // after await repo_relate(src, "linked_to_note", tgt)
Defensive patterns
Strategy: validation
Validate before calling
import re
IDENT = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
def safe_name(n: str) -> bool:
return isinstance(n, str) and bool(IDENT.match(n))
assert safe_name(relationship) before calling repo_relate Type guard
def is_safe_identifier(value) -> bool:
return isinstance(value, str) and bool(_IDENTIFIER_RE.match(value)) Try / catch
try:
await repo_relate(src, rel, tgt)
except ValueError as e:
if 'Invalid' in str(e) and 'name' in str(e):
raise InvalidInputError(str(e)) # 400 to client
raise Prevention
- Never build table/relationship names from raw user input
- Keep table names as module-level constants
- Never pass RecordID objects where a table name is expected
When it happens
Trigger: Passing a table or relationship name with a space, hyphen, dot, or leading digit to repo_relate/repo_upsert/repo_update; passing None or a non-string (e.g. a RecordID or enum object) where a name is expected; dynamically building relationship names from untrusted input.
Common situations: Dynamically constructing relationship names from note types or user input; passing a full record id ('note:xyz') instead of a table name; refactors that change a constant into an f-string; SurrealDB records created with quoted/escaped identifiers that don't match the regex.
Related errors
- Invalid order_by field: '{parts[0]}'
- Invalid order_by clause: '{clause.strip()}'
- Error fetching chat sessions: {str(e)}
- Error creating chat session: {str(e)}
- Error fetching session: {str(e)}
AI-assisted analysis of lfnovo/open-notebook@a7de90d38a (2026-08-27).
Data as JSON: /api/errors/0423ec717325de68.
Report an issue: GitHub.