Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / Stdlib

Guid.* — the empty id, a fresh id, and reading one out of a string

Guid.Empty · Guid.NewGuid() · Guid.Parse(s)

A `Guid` is the type an entity's `Id` has, and the type a reference to a row has. `Guid.NewGuid()` mints a fresh one, `Guid.Empty` is the all-zero value, and `Guid.Parse(s)` reads one out of text — a route parameter, a query string, a form field. `Parse` REFUSES text that is not a Guid rather than answering the all-zero id, and a `string` never converts to a `Guid` on its own: you write the call, exactly as in C#.

stable2 examples compiled by CIstdlibguididrouting

Summary#

A route parameter arrives as a string. An entity's Id is a Guid. Guid.Parse is the call between them:

entity Doc {
  [MaxLength(120)] string Title;
}

[Server, AllowAnonymous] Doc Fetch(Guid id) { return Doc.Single(d => d.Id == id); }

[Page("/doc/{id}"), AllowAnonymous]
component DocPage(string id) {
  render { Button("Load", onPress: Load); }
  action Load() { var doc = Fetch(Guid.Parse(id)); }
}

You can often skip it entirely — declare the route parameter as a Guid and the platform parses it for you before your component ever runs. Reach for Guid.Parse when the text comes from somewhere the platform has not already typed: a form field, a value you built, a string you were handed.

Signature#

Guid Guid.Empty                 // the all-zero id — a static VALUE, written without parentheses
Guid Guid.NewGuid()             // a fresh random id
Guid Guid.Parse(string s)       // the string as a Guid — REFUSES text that is not one

Description#

Guid.Parse refuses, it does not coerce#

Guid.Parse("nope") fails the call. It does not answer Guid.Empty, and that is the whole point: an all-zero id silently substituted for a mistyped URL would be a confident lookup of a row that does not exist, which is a wrong answer rather than a missing one. This is C#'s contract for Parse, and Osy# keeps it.

There is no Convert.ToGuid. The coercing Convert.To* family exists for the numeric types, where a blank form field genuinely means zero; there is no value a Guid can fall back to that means "blank".

Which id formats does Guid.Parse accept?#

Thirty-two hex digits, with or without the four dashes, in either letter case, with surrounding whitespace trimmed. The answer is always the canonical lowercase dashed form, so a parsed id and one the server sent are the same string:

Guid.Parse("0f8fad5b-d9cb-469f-a165-70867728950e")   // ✅ the canonical form
Guid.Parse("0F8FAD5B-D9CB-469F-A165-70867728950E")   // ✅ → lowercased
Guid.Parse("0f8fad5bd9cb469fa16570867728950e")       // ✅ bare → comes back DASHED
Guid.Parse("  0f8fad5b-…-70867728950e  ")            // ✅ trimmed

Guid.Parse("{0f8fad5b-…-70867728950e}")              // ❌ braces are not accepted
Guid.Parse("(0f8fad5b-…-70867728950e)")              // ❌ nor parentheses
Guid.Parse("")                                       // ❌ empty is not Guid.Empty

This is narrower than .NET's Guid.Parse, on purpose. .NET also takes the brace, parenthesis and {0x…,0x…,{0x…}} forms. Osy# runs your code on several engines — the server, the browser, and compiled JavaScript — and the accepted set has to be identical on all of them or the same string parses in one place and fails in another. A set that large cannot be matched honestly across all of them, so the contract is the part every engine can meet exactly. Nothing the platform produces is ever in one of the refused forms.

A string never becomes a Guid on its own#

void Load(Guid id) { … }

Load(routeParam);              // ❌ refused — cannot pass 'string' to a parameter of type 'Guid'
Load(Guid.Parse(routeParam));  // ✅

The same holds in every position — a return, a field write, an array element, a class initializer — and in the other direction too: a Guid where a string is wanted needs .ToString() or an interpolation ($"{id}").

A comparison inside a query is a different rule and still works. Doc.Single(d => d.Id == someString) resolves — the query engine compares the underlying values — so the shape a page is built from is unaffected.

Guid.TryParse — write it as you would in C##

Guid.TryParse(s, out var id) compiles. Osy# has no out parameter of its own — a call here can suspend and resume elsewhere, so no caller frame is guaranteed to still be waiting for a write-back — but the compiler recognises this one shape and rewrites it into the lines it means, ahead of the statement that holds it:

if (Guid.TryParse(text, out var id)) { … }

Guid? id = null;                                 // ← the rewrite
try { id = Guid.Parse(text); } catch { }
if (id != null) { … }

So id is a Guid?, in scope for the rest of the block exactly as C# scopes an out var.

Two positions are refused, and each says so with the form that works there: a loop CONDITION (the parse would run once instead of per iteration — test inside the body and break) and a lambda body (it belongs to the element — put it in a function and call that). out into a variable that ALREADY exists is also refused: C# writes default(T) there on failure and this language has no spelling for the zero of every type.

Where the text should already be valid, Guid.Parse on its own is the honest call — let the failure be a failure. Where a route parameter may be junk, declare it a Guid and let the platform reject the request before your code runs.

Where these run#

All three run in the browser — no round trip. Guid.NewGuid() is deliberately non-deterministic and is memoized across a durable resume, so a re-entered function does not mint a second id after handing out the first.

Guid.Parse has no SQL push-down: using it inside a query predicate over a database column is refused at compile time rather than translated, because Postgres accepts text this contract does not.

Examples#

entity Invite {
  Guid Token;
  [MaxLength(60)] string Label;
}

Invite Open(string label) {
  return new Invite { Token = Guid.NewGuid(), Label = label };
}

string Show(Guid token) { return token.ToString(); }

Invite Redeem(string token) {
  var t = Guid.Parse(token);
  return Invite.Single(i => i.Token == t);
}

See also#

Related

The standard library — and where each call runs

The pure standard library, and the one fact about it that changes how your app feels: 126 of its 155 methods run in the…

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

entity members

The typed members an entity holds — text, numbers, dates, booleans, Guids, enums and references. A member's type…