에러 코드 — 디스패치와 장부
각 절은 ### QuadNNNN — 메시지 원문(자리표시자는 {…}), 언제 나는가, 어떻게 고치는가. 번호로 찾으려면 색인.
Quad0019
섹션 제목: “Quad0019”Bookkeeping.{fnName}: position must be a positive integer (got {tostring(i)}) — quad-base/src/Bookkeeping.luau
- 언제:
q.Bookkeeping.setLength/setOffsetSource/setEmpty/getOffsetAt등 위치(i/at)를 받는 공개 함수에 양의 정수가 아닌 값을 넘겼을 때.{fnName}은 실제로 부른 함수 이름입니다. - 고치려면: 1부터 시작하는 정수를 넘기세요.
- 참고: Length/Offset 부기
Quad0020
섹션 제목: “Quad0020”Bookkeeping.{fnName}: ownerKey must not be nil — quad-base/src/Bookkeeping.luau
Bookkeeping.claimOwnerAt: inst (ownerKey) must not be nil — quad-base/src/Slot/Owner.luau
Bookkeeping.releaseOwner: ownerKey must not be nil — quad-base/src/Slot/Owner.luau
- 언제:
q.Bookkeeping의 부기 함수(getBookkeeping/getOffsetAt/setLength/setOffsetSource/setEmpty)에ownerKey(부기의 주인 — 요소 또는 Slot)로nil을 넘겼을 때.{fnName}은 부른 함수 이름입니다. - 언제:
claimOwnerAt의inst(그 자리의 주인 — ownerKey)나releaseOwner의ownerKey로nil을 넘겼을 때. - 고치려면: 실제 owner를 넘기세요.
- 참고: Length/Offset 부기
Quad0021
섹션 제목: “Quad0021”Bookkeeping.{fnName}: length must be a non-negative integer (got {tostring(n)}) — quad-base/src/Bookkeeping.luau
- 언제:
q.Bookkeeping.setLength(ownerKey, i, len, ...)의len(상수인 경우),State인 경우 등록하는 순간의 현재 값(그setLength줄에서, 부기를 쓰기 전에), 또는 그 뒤 그State가 내놓은 값(그:Set줄에서)이 음수/소수/숫자가 아닐 때. - 고치려면: 0 이상의 정수를 쓰세요.
- 참고:
q.Bookkeeping.setLength(ownerKey, i, len, anchor, element)
Quad0022
섹션 제목: “Quad0022”Bookkeeping.getOffsetAt: position {at} is past N+1 (N = {last}, at most {last + 1} may be queried) - or a leaf handler skipped its position registration: position {i} is not registered — quad-base/src/Bookkeeping.luau
- 언제:
q.Bookkeeping.getOffsetAt(ownerKey, at)으로 조회한at이 등록된 마지막 위치(N) + 1보다 클 때 — 범위 밖 조회입니다. (같은 자리에서 중간의 어떤 위치가 등록 자체를 건너뛴 경우도 이 갈래로 잡힙니다 — 두 원인을 코드가 구분할 수 없어 메시지가 둘 다 말합니다.)q.Bookkeeping.setOffsetSource(ownerKey, i, source)도 내부에서getOffsetAt(ownerKey, i)를 부르므로,i앞 위치들이 아직 등록되지 않았으면 같은 메시지가 납니다 — 이때 메시지는 직접 부르지 않은getOffsetAt을 말합니다. - 고치려면: 등록된 범위(
1..N+1) 안에서 조회하세요. 조회나setOffsetSource전에 그 앞 위치들을 계약 순서대로(위치마다setOffsetSource→setLength, 또는setEmpty하나) 먼저 등록하세요 — 위치를 등록하는 것은setLength/setEmpty뿐이고setOffsetSource는 등록하지 않고 앞 위치를 조회합니다. 앞 위치를setLength만으로 등록하면 이 에러 대신Quad0023(그 위치의 발행 채널이 없음)이 납니다. - 참고:
q.Bookkeeping.getOffsetAt(ownerKey, at)
Quad0023
섹션 제목: “Quad0023”Bookkeeping.recompute: sourceList[{i}] is nil - a nil hole in the numeric-key part of props ({ a, nil, b })? fill the optional slot with q.None (the children placed before this raise stay seated in the half-built Instance, reachable as child.Parent - q.dispose that Instance to clean up; it takes those children with it, so build the retry with new ones); if you are writing a handler, bookkeeping is broken (setLength without setOffsetSource? the contract says None) — quad-base/src/Bookkeeping.luau
- 언제: 재계산 도중 위치
i의 오프셋 발행 채널(sourceList[i])이nil일 때 — 대표 원인은 배열 리터럴 중간에nil이 낀 props({ a, nil, b })이고, 핸들러를 직접 쓰는 입장이면setOffsetSource(또는setEmpty) 등록을 빠뜨린 경우입니다. - 고치려면: 사용자라면 그 옵셔널 자리를
q.None으로 채우세요. 핸들러 작성자라면 그 자리의setOffsetSource등록을 빠뜨리지 않았는지 확인하세요. - 관측(계약 아님): 이 에러가 난 뒤 그 Instance는 반쯤 지어진 채(구멍 뒤 자식까지 붙어 있음 — 메시지의 “children placed before this raise”보다 넓음) 남습니다. 같은 자식 값으로 다시 만들면
Quad0156, 그 Instance를 dispose한 뒤 다시 쓰면Quad0219가 납니다. - 참고: Length/Offset 부기
Quad0024
섹션 제목: “Quad0024”Bookkeeping.recompute: lengthList[{i}] is nil - bookkeeping is broken (setOffsetSource without setLength?) — quad-base/src/Bookkeeping.luau
- 언제: 재계산 도중 위치
i의 길이(lengthList[i])가nil일 때 — 내부 불변식 위반으로, 어떤 핸들러가setOffsetSource만 부르고setLength는 부르지 않은 경우입니다. - 고치려면: 그 핸들러가
setLength도 함께 부르게 하세요(또는 둘 다 하는setEmpty를 쓰세요). - 참고: Length/Offset 부기
Quad0025
섹션 제목: “Quad0025”Bookkeeping.setOffsetSource: source must be a Source<number> or None (got {typeof(source)}) — quad-base/src/Bookkeeping.luau
- 언제:
q.Bookkeeping.setOffsetSource(ownerKey, i, source)의source가Source<number>도q.None도 아닐 때. - 고치려면: 부기 채널로
Source<number>나q.None(발행 채널 없음)을 넘기세요. - 참고:
q.Bookkeeping.setOffsetSource(ownerKey, i, source)
Quad0229
섹션 제목: “Quad0229”Bookkeeping.getOffsetAt: position {at} needs positions 1..{at - 1} registered but {i} is not - a leaf handler skipped its position registration — quad-base/src/Bookkeeping.luau
- 언제: 조회한
at은 등록 범위 안(N+1이하)인데, 그 앞의 어떤 위치i가 등록되지 않았을 때 — 어떤 leaf 핸들러가 자기 자리의 위치 등록(setLength/setOffsetSource)을 건너뛴 경우입니다.getOffsetAt의 if/else 두 갈래 중 하나로, 같은 조건문의 다른 갈래가 Quad0022입니다. 실제로는 배치 차단기(drive·Slot 내부가 켜는 것)가 켜져 재계산이 미뤄진 동안에만 이 갈래가 보입니다 — 차단기 밖에서 구멍이 생기면setLength/setEmpty끝의 재계산이 Quad0023/Quad0024로 먼저 던집니다. - 고치려면: 그 자리를 담당한 핸들러가 반드시
setLength와setOffsetSource(또는 그 둘을 한 번에 하는setEmpty)를 부르게 하세요. - 참고:
q.Bookkeeping.getOffsetAt(ownerKey, at)
Quad0049
섹션 제목: “Quad0049”Modifier:Peek: key must be a non-empty string (got {if key == "" then '""' else typeof(key)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:Peek(key)를 부를 때key가 빈 문자열이거나 문자열이 아닌 경우. setter 경로(mod:Field(...))가 거부하는 것과 같은 키 도메인이라, 예전에는Peek이 그런 키에 조용히nil을 돌려주던 것을 바로잡았습니다. - 고치려면: 필드 이름은 비어있지 않은 문자열로 넘기세요.
- 참고:
mod:Peek<<T>>(key)
Quad0050
섹션 제목: “Quad0050”Modifier:Apply: factory must be a function (got {typeof(factory)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:Apply(factory)의factory가 함수가 아닐 때. - 고치려면: Modifier를 받아 값을 돌려주는 함수를 넘기세요.
- 참고:
mod:Apply(factory)
Quad0051
섹션 제목: “Quad0051”Modifier:As: name must be a class name string (got {typeof(name)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:As(name)에서name을 줬는데 문자열이 아닐 때(생략은 허용됩니다). - 고치려면: 클래스 이름 문자열을 넘기거나 생략하세요.
- 참고:
mod:As(name)
Quad0052
섹션 제목: “Quad0052”Modifier: unknown modifier class "{name}" - Modifier.TypedFactory/DefineSubtype it first — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:As(name)에 준name이 아직Modifier.TypedFactory나DefineSubtype으로 등록되지 않은 클래스일 때. - 고치려면: 먼저
q.Raw.Modifier.TypedFactory(name)이나DefineSubtype으로 그 이름을 등록하세요. - 참고:
mod:As(name)
Quad0053
섹션 제목: “Quad0053”Modifier.Overridden: must be given at least one Modifier — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
q.Raw.Modifier.Overridden(...)을 인자 없이 부를 때. 콜론 형태mod:Overridden()은mod자신이 첫 인자로 들어가므로 이 에러가 나지 않습니다. - 고치려면: 합칠 Modifier를 최소 하나 넘기세요.
- 참고:
q.Raw.Modifier.Overridden(...)
Quad0054
섹션 제목: “Quad0054”Modifier.Overridden: argument #{i} is not a Modifier (got {typeof(arg)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
Overridden에 넘긴 인자 중 하나가 Modifier가 아닐 때 —q.Raw.Modifier(...)와 달리 평범한 필드 테이블은 받지 않습니다. - 고치려면: 모든 인자를 Modifier로 넘기세요 — 평범한 테이블을 합치려면
q.Raw.Modifier(...)를 쓰세요. - 참고:
q.Raw.Modifier.Overridden(...)
Quad0055
섹션 제목: “Quad0055”Modifier: unknown modifier class "{target}" - Modifier.TypedFactory/DefineSubtype it first (or use :As(name) for an unchecked cast) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:As<Class>()검사형 다운캐스트의 대상 클래스가 아직 등록되지 않았을 때. - 고치려면: 대상 클래스를
TypedFactory/DefineSubtype으로 먼저 등록하세요. 메시지가 안내하는mod:As(name)은 조상 검사만 건너뛰고 이름이 등록됐는지는 똑같이 검사하므로, 등록되지 않은 이름에는:As(name)도 Quad0052로 거부됩니다. - 참고:
mod:As<Class>()
Quad0056
섹션 제목: “Quad0056”Modifier: cannot cast a "{tag}" modifier to "{target}" - not an ancestor (use :As(name) to force) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:As<Class>()의 대상이 등록은 돼 있지만, 지금 Modifier의 태그가 무타입도 대상 자신도 대상의 조상도 아닐 때. - 고치려면: 실제 조상/자손 관계를 확인하거나, 조상 검사 없이 강제로 태그를 바꾸려면
mod:As(name)을 쓰세요. - 참고:
mod:As<Class>()
Quad0057
섹션 제목: “Quad0057”Modifier: field "{key}" cannot hold a handler-layer value (Ref/Observer/Effect/Slot/Modifier/Spread/mapper descriptor) — quad-base/src/Dispatch/Modifier/init.luau
Modifier: field "{k}" cannot hold a handler-layer value (Ref/Observer/Effect/Slot/Modifier/Spread/mapper descriptor) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod:Field(value)의value(리터럴이든 변환 함수의 반환이든)가Ref/PreRef/PostRef/Observer/Effect/Slot/Modifier/Spread같은 핸들러 계층 값이거나Claim/Mapper의 디스크립터(q.Mapper "Frame" ("Title")등 — 숫자 키 자리에서만 해석되는 값)일 때.State/Source는 통과합니다(State가 디스크립터를 담은 경우는 만들 때 값을 읽지 않으므로 여기서 걸리지 않습니다). - 언제: 초기 필드 테이블의 값이 핸들러 계층 값(
Ref/Observer/Effect/Slot/Modifier/Spread)이거나Claim/Mapper의 디스크립터일 때 — setter 경로와 같은 규칙을 생성자에도 적용합니다. - 고치려면: 그 값은 필드가 아니라 props의 숫자 키 자리에 놓으세요.
- 참고:
mod:<Field>(value),q.Raw.Modifier(...)
Quad0058
섹션 제목: “Quad0058”Modifier: setter keys must be strings (got {typeof(key)}) - put a non-string key in the props table itself (Frame { [key] = value }) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
mod[key](...)를 문자열이 아닌key로 부를 때(AttrKey/MapperDescriptor같은 비문자열 키). 생성자의 같은 규칙(Quad0059)과 짝입니다. - 고치려면: 그 키는 Modifier가 아니라 props 테이블에 직접(
[key] = value) 넣으세요. - 참고:
mod:<Field>(value)
Quad0059
섹션 제목: “Quad0059”Modifier: initial-field table keys must be non-empty field names (got {if k == "" then '""' else typeof(k)} at argument #{i}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
q.Raw.Modifier{ ... }/q.Modifier "Class" { ... }생성자에 넘긴 필드 테이블의 키가 빈 문자열이거나 문자열이 아닐 때. - 고치려면: 필드 이름은 비어있지 않은 문자열로 쓰세요.
- 참고:
q.Raw.Modifier(...)
Quad0060
섹션 제목: “Quad0060”Modifier: field "{k}" collides with a reserved Modifier method — quad-base/src/Dispatch/Modifier/init.luau
- 언제: 초기 필드 테이블의 키가
Peek/Apply/Overridden/As같은 예약 메소드 이름과 같을 때 — setter 경로에서는 애초에 만들어질 수 없는 필드라 생성자도 같은 이름을 거부합니다. - 고치려면: 그 이름을 피하세요 — 예약 메소드는 필드가 될 수 없습니다.
- 참고:
q.Raw.Modifier(...)
Quad0061
섹션 제목: “Quad0061”Modifier: field "{k}" matches the reserved cast prefix As<Class> - casts are methods, not fields — quad-base/src/Dispatch/Modifier/init.luau
- 언제: 초기 필드 테이블의 키가
As+ 대문자로 시작하는 예약된 캐스트 접두사 모양일 때. - 고치려면: 그 이름을 피하세요 —
As<Class>모양은 항상 캐스트 메소드로 예약돼 있습니다. - 참고:
q.Raw.Modifier(...)
Quad0062
섹션 제목: “Quad0062”Modifier: field "{k}" starts with "__" - metamethod-looking names are never properties — quad-base/src/Dispatch/Modifier/init.luau
- 언제: 초기 필드 테이블의 키가
"__"로 시작할 때 — setter 경로(__index)도 그런 이름엔 항상nil을 돌려주므로 생성자에서도 같은 이름을 막습니다. - 고치려면:
"__"로 시작하지 않는 필드 이름을 쓰세요. - 참고:
q.Raw.Modifier(...)
Quad0063
섹션 제목: “Quad0063”Modifier: field "{k}" in an initial-field table cannot be a function - a transform goes through mod:{k}(fn); an event handler does not belong in a Modifier (put it as an inline key on the element, or wrap it in a State) — quad-base/src/Dispatch/Modifier/init.luau
Modifier: the transform for field "{key}" returned a function - a field cannot hold a function (an event handler does not belong in a Modifier; put it as an inline key on the element, or wrap it in a State) — quad-base/src/Dispatch/Modifier/init.luau
- 언제: 초기 필드 테이블의 값으로 함수를 줬을 때 — setter 자리에서 함수는 변환 함수라는 다른 뜻이라, 생성자에서 그대로 받으면 두 경로가 조용히 갈립니다.
- 언제:
mod:Field(fn)의 변환 함수가 함수를 돌려줬을 때(지금 필드 값이 평범한 값인 갈래) — 흔히q.Raw.Modifier():Activated(function(_old) return function() … end end)처럼 이벤트 핸들러를 Modifier에 넣으려는 우회입니다. 지금 필드 값이State면 변환이:Compute로 미뤄져 여기서 걸리지 않습니다. - 고치려면: 변환이 필요하면
mod:{k}(fn)을 쓰되 함수가 아닌 값을 돌려주게 하고, 이벤트 핸들러라면 Modifier가 아니라 요소의 인라인 키(q.Elem "TextButton" { Activated = fn })로 넘기세요. 꼭 Modifier로 옮겨야 하면State에 담아 넘기세요. - 참고:
q.Raw.Modifier(...),mod:<Field>(value)
Quad0065
섹션 제목: “Quad0065”Modifier: argument #{i} must be a Modifier or a plain field table (got {typeof(arg)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
q.Raw.Modifier(...)/q.Modifier "Class" (...)에 넘긴 인자가 Modifier도, 메타테이블 없는 평범한 필드 테이블도 아닐 때(Source/Ref/Tag같은 다른 quad 값을 넘긴 경우 등). - 고치려면: Modifier나 평범한
{ field = value }테이블만 넘기세요. - 참고:
q.Raw.Modifier(...)
Quad0066
섹션 제목: “Quad0066”Modifier.{fn}: {what} must be a non-empty class name string (got {typeof(name)}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
q.Raw.Modifier.TypedFactory(name)의name이나q.Raw.Modifier.DefineSubtype(parent, subtype)의parent/subtype이 빈 문자열이거나 문자열이 아닐 때.{fn}/{what}은 실제로 부른 함수와 검사한 인자 이름입니다. - 고치려면: 비어있지 않은 클래스 이름 문자열을 넘기세요.
- 참고:
q.Raw.Modifier.TypedFactory<<T>>(name)
Quad0067
섹션 제목: “Quad0067”Modifier.DefineSubtype: cannot make "{subtype}" a subtype of "{parent}" - that would make a cycle — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
q.Raw.Modifier.DefineSubtype(parent, subtype)이 등록하려는 간선이 이미 있는 조상 관계를 거슬러 올라가 순환을 만들 때. - 고치려면: 순환이 생기지 않는 방향으로 등록하세요.
- 참고:
q.Raw.Modifier.DefineSubtype(parent, subtype)
Quad0068
섹션 제목: “Quad0068”Modifier: a Modifier cannot be a value of key "{tostring(k)}" - place it in the array part — quad-base/src/Dispatch/Modifier/init.luau
Effect: must be an array item, not the value of a {typeof(k)} key — quad-base/src/Effect.luau
Observer: must be an array item, not the value of a {typeof(k)} key — quad-base/src/Observer.luau
{kind}: must be an array item, not the value of a {typeof(k)} key — quad-base/src/Ref/init.luau
Dispatch.drive: a Spread cannot be the value of key "{tostring(k)}" - place it in the array part — quad-base/src/Dispatch/Modifier/init.luau
Spread: a Spread cannot be the value of key "{tostring(k)}" - place it in the array part — quad-base/src/Dispatch/Spread.luau
- 언제: Modifier를 props의 문자 키 값 자리에 뒀을 때 — 합칠 대상이 없는 자리입니다. 1부터 빈틈 없이 이어지는 배열부 밖의 숫자 키(
{ [5] = mod }처럼 앞 자리가 비어 있는 키)도 배열부가 아니라서 같은 에러가 납니다 — 메시지에는 그 숫자가 키 이름으로 나옵니다. - 언제:
Effect를 props의 문자 키 값 자리에 두거나,State/Store에 담아 그 자리에 닿게 했을 때. 단 그 키가 클래스의 반영 프로퍼티 이름이면 프로퍼티 핸들러가 먼저 받아 이 가드에 닿지 않습니다 —q.debug를 켜면 그 자리에서 경고 한 줄이 나옵니다. - 언제:
Observer를 props의 문자 키 값 자리에 두거나,State/Store에 담아 그 자리에 닿게 했을 때. 단 그 키가 클래스의 반영 프로퍼티 이름이면 프로퍼티 핸들러가 먼저 받아 이 가드에 닿지 않습니다 —q.debug를 켜면 그 자리에서 경고 한 줄이 나옵니다. - 언제:
PreRef/PostRef를 props의 문자 키 값 자리에 두었을 때(평범한Ref는 이 가드가 없습니다 —Quad0076이 문자 키 꼬리로 같은 안내를 합니다) — 단 그 키가 클래스의 반영 프로퍼티나 이벤트 이름이면 프로퍼티·이벤트 핸들러가 먼저 받아 이 가드에 닿지 않습니다(Name = ref는 핸들러의 번호 없는 타입 에러,Size = ref는 조용히 대입,Activated = ref는Quad0218— mock에서의 관측, 계약 아님; 생성 props 타입이 strict에서 이 자리를 거부합니다). 그런 자리는q.debug를 켜면 경고 한 줄({kind}: found at string key …)로 드러납니다. - 언제: Spread를 props의 문자 키 값 자리에 뒀을 때 — 펼칠 수 있는 자리는 숫자 키(배열부)뿐입니다. 1부터 빈틈 없이 이어지는 배열부 밖의 숫자 키도 같은 에러가 납니다.
- 언제: Spread를 만드는 테이블에서 다른 Spread를 문자 키 값 자리에 뒀을 때 — 펼칠 수 있는 자리는 숫자 키(배열부)뿐입니다. 1부터 빈틈 없이 이어지는 배열부 밖의 숫자 키도 같은 에러가 납니다.
- 고치려면: 그 값을 1부터 빈틈 없이 이어지는 숫자 키(배열부) 자리에 놓으세요.
Effect·Observer·PreRef·PostRef는 리터럴로 그 자리에 놓습니다. 비어 있을 수 있는 Spread는props.FieldSpread or q.None처럼None으로 자리를 채우고, 안쪽 Spread는q.Raw.Spread { Visible = true, inner }처럼 놓습니다. - 참고: Modifier — 숫자 키 자리에 놓는다, Observer·Effect — 숫자 키 자리에 넣기, Ref — 숫자 키 자리에만 놓는다,
q.Spread "Class" (props)
Quad0069
섹션 제목: “Quad0069”{kind}: already used - a {kind} is one-shot (used up once quad places it, fired or not); make a new one for each instance — quad-base/src/Dispatch/RefPrePass.luau
- 언제: 이미 한 번 props 숫자 키 자리에 놓여 처리된
PreRef/PostRef를 또 놓았을 때 — 다른 인스턴스의 props든, 같은 props 안의 두 자리({ p, p })든 마찬가지입니다. 표시는 quad가 자리에 놓인 값을 처리하는 순간 붙으므로PostRef는 아직 발화하기 전이어도 해당하고, 사용자가 직접ref:Set(inst)만 한 값은 해당하지 않습니다.{kind}는PreRef또는PostRef입니다. - 고치려면: 인스턴스마다 새
PreRef/PostRef를 만드세요 — 하나를 재사용하지 마세요. - 참고: 숫자 키 자리에만 놓는다
Quad0070
섹션 제목: “Quad0070”State: the value chain is circular (a State that eventually holds itself) - a State may hold another State, but not a cycle — quad-base/src/StateHandler.luau
- 언제:
q.debug가 켜져 있을 때, props 자리에 놓인 State의 값을 따라가다(State가 다른 State를 값으로 들고 있으면 그 State도 마저 벗기는 재귀) 결국 자기 자신으로 돌아오는 순환을 만났을 때(예:a:Set(b); b:Set(a)). Slot에 넣은 State도 같습니다 — 원소 State의 값 사슬이 순환하면(st:Set(st)등) Slot이 그 값을 읽는 때 — 마운트될 때(이미 마운트된 Slot이면 넣는 줄)나 그:Set줄 — 던집니다. 마운트 전 Slot에 넣는 줄에서는 던지지 않습니다 — 그 시점엔 원소 State의 값을 읽지 않으므로(지연 계산), debug에서도 값을 앞당겨 읽지 않습니다. 사슬을 끝까지 따라가는 이 검사는 emit마다 도는 자리라 debug 층에 있습니다 —q.debug를 꺼 두면 검사하지 않고, 순환은 스택 오버플로로 끝나는 정의되지 않은 동작입니다(Slot 원소 자리라면 그 Slot이 망가진 채 남을 수 있습니다). 개발 중에는 debug를 켜 두세요. - 관측(계약 아님): Slot 원소 자리에서 던진 뒤 그 Slot은 지금 이전 원소를 유지한 채 다음 정상
:Set으로 이어지는 것이 관측됩니다. - 고치려면: State가 다른 State를 값으로 들고 있는 것 자체는 되지만, 그 사슬이 순환하면 안 됩니다 — 순환이 생기지 않게 값 대입을 정리하세요.
- 참고: Quadnomicon Vol. 8 — 확장 가능한 디스패치 엔진 ·
slot:Add
Quad0071
섹션 제목: “Quad0071”Dispatch.retractFrom: no slot at index {i} - the chain array has a hole, bookkeeping is broken — quad-base/src/Dispatch/init.luau
- 언제:
retractFrom이 체인 배열을 되감다가 구멍(nil슬롯)을 만났을 때 — 계약을 지키는 호출로는 나지 않는 내부 불변식 위반입니다. 다만 계약을 어긴 호출은 공개 함수retractFrom을 거쳐 이 상태에 닿을 수 있습니다 — 위임 핸들러의 안쪽 retractor가 같은(inst, key)에retractFrom을 다시 부르는 간접 재진입(정의되지 않은 동작). - 고치려면: retractor 안에서 같은 자리를 다시 철거하지 마세요(재진입은 계약 밖). 그런 코드가 없는데 났다면 quad 내부 불변식 위반입니다 — 재현되면 보고해 주세요.
- 참고:
q.Dispatch.retractFrom(inst, key, index)
Quad0072
섹션 제목: “Quad0072”Dispatch.{fnName}: inst must not be nil — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.process/q.Dispatch.retractFrom/q.Dispatch.getHandler를inst = nil로 직접 부를 때.{fnName}은 실제로 부른 함수 이름입니다. - 고치려면: 실제 대상 요소(
inst)를 넘기세요. - 참고:
q.Dispatch.process(inst, key, value, index)
Quad0073
섹션 제목: “Quad0073”Dispatch.{fnName}: index must be a positive integer (got {tostring(index)}) — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.process/q.Dispatch.retractFrom의index가 1 이상의 정수가 아닐 때. - 고치려면: 체인 깊이는 1부터 시작하는 정수입니다 — 최초 진입은
1, 위임은index + 1을 넘기세요. - 참고:
q.Dispatch.process(inst, key, value, index)
Quad0074
섹션 제목: “Quad0074”Dispatch.{fnName}: key must not be nil — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.process/q.Dispatch.retractFrom/q.Dispatch.getHandler를key = nil로 부를 때. - 고치려면: 실제 props 키(숫자 또는 문자열)를 넘기세요.
- 참고:
q.Dispatch.process(inst, key, value, index)
Quad0075
섹션 제목: “Quad0075”Dispatch.process: index {index} would leave a gap in the chain (the chain has {#list} slots - a delegating handler passes index + 1) — quad-base/src/Dispatch/init.luau
- 언제:
process에 준index가 그 자리 체인의 다음 자연스러운 깊이(#list + 1)보다 클 때 — 위임 핸들러가index + 1이 아닌 값을 건너뛰어 부른 경우입니다. - 고치려면: 위임할 때는 항상 받은
index + 1을 그대로 넘기세요. - 참고:
q.Dispatch.process(inst, key, value, index)
Quad0076
섹션 제목: “Quad0076”Dispatch: no handler matched key {tostring(k)} (value: {typeof(v)}[, brand: {brands}]) + 꼬리 힌트 하나(+ nil 안내) — brand:는 브랜드가 있을 때만 붙습니다 — quad-base/src/Dispatch/Diagnostics.luau(던지는 자리는 quad-base/src/Dispatch/init.luau)
brand: 뒤에는 그 값이 답하는 브랜드 이름이 전부, 이름순으로 쉼표로 이어 붙습니다 — 모듈의 is<Brand> 필드 가운데 q.BrandPredicates에 등록된 술어만 묻습니다(quad-base의 q.isX 술어도 모듈이 만들어질 때 같은 집합에 등록됩니다). 그래서 q.PreRef()는 Epoch, PreRef, Ref, q.Source(1)은 Epoch, Source, State처럼 포함 관계에 있는 이름이 함께 나옵니다. 등록되지 않은 is* 헬퍼는 묻지 않고, 등록된 술어가 던지면 그 술어는 걸리지 않은 것으로 칩니다.
꼬리 힌트는 이렇게 정해집니다. 값이 q.Protocols.DispatchHint에 속하면 그 값의 __dispatchHint(key)를 한 번 불러 돌려받은 문장을 - 뒤에 씁니다 — 브랜드가 자기 쓰임을 직접 말합니다. quad가 등록한 문장은 이렇습니다(문구는 API가 아닙니다):
| 값 | 꼬리 |
|---|---|
Store |
- a Store is not a value for any key: use one of its fields (store.Name) or wrap it (q.Attr(store)) |
Context |
- a Context is not a value for any key: read the value you need out of it first |
Provider |
- a Provider is not a value for any key: read the value you need out of the Context first |
Blocker |
- a Blocker is not a value for any key: apply it to a State (state:Apply(blocker)) and place that State |
AttrKey |
- an AttrKey is a key, not a value: use it as the key ([q.AttrKey(name)] = value) |
Tween(quad-roblox, 문자 키) |
- this key may not be a field of this element (a typo, a read-only field, or one from another class) |
훅이 nil을 돌려주거나(숫자 키의 Tween) 값이 DispatchHint에 속하지 않으면 일반 꼬리 다섯 중 하나입니다 — 값이 등록된 브랜드 술어에 하나도 걸리지 않고 키가 문자 키면 - this key may not be a field of this element (a typo, a read-only field, or one from another class), or the provider that handles it (e.g. quad-roblox) is not initialized(프로바이더가 설치된 흔한 경우엔 앞쪽 원인 — 오타 난 이름, 읽기 전용 프로퍼티, 다른 클래스의 이름 — 이고, 두 경우를 이 자리에서 가를 수 없어 둘 다 적습니다), 같은 값이 숫자 키면 — 값이 함수일 때는 - a function in an array slot: a constructor or component that was not called? (a missing {} or ())(숫자 키 자리에서 함수를 받는 핸들러는 없어서, 거의 늘 { q.Elem "Frame" }처럼 호출을 빠뜨린 경우입니다), 그 밖에는 - check that the provider for this value (e.g. quad-roblox) is initialized; 등록된 술어에 하나라도 걸리면 — 등록됐다는 것은 그 브랜드의 주인(quad-base는 늘, 프로바이더는 설치 때 등록)이 설치돼 있다는 뜻이라 quad-base 브랜드(Slot/Ref/State/Tag/Attr/MapperDescriptor 등)와 프로바이더 브랜드가 같은 힌트를 받습니다 — 문자 키일 때 - a quad value at a string key: it belongs in a numeric (array) slot, 숫자 키일 때 - this quad value has no handler at an array position. 값이 nil이면 그 뒤에 (a nil at this depth may be an unwrapped None or a reactive nil - the key's own handler must accept nil)이 더 붙습니다. brand: … 조각은 등록된 술어에 걸린 것이 있을 때만 들어갑니다.
- 언제: 그 값을 받아줄 핸들러가 하나도 없을 때.
noMatchMessage헬퍼가 만드는 문구이고,Dispatch.process의 매치 실패 raise가 이 헬퍼를 부릅니다. - 고치려면: 꼬리가 “a function in an array slot”이면 그 자리의 생성자나 컴포넌트를 호출했는지 보세요(
q.Elem "Frame"뒤의{}, 컴포넌트 뒤의()). 프로바이더(quad-roblox 등)가 설치돼 있다면 가장 흔한 원인은 문자 키 쪽입니다 — 그 클래스에 없는(오타 난) 프로퍼티·이벤트 이름, 읽기 전용 프로퍼티(AbsoluteSize등 — 읽으려면q.OnChange), 그리고Parent(props가 아니라 만든 뒤 밖에서inst.Parent = …로 붙입니다)를 확인하세요. quad 값이라면 올바른 종류의 키(숫자/문자) 자리에 놓으세요(배열 원소로만 쓰는 값 —Tag·Attr등 — 이 반영 프로퍼티 이름 키에 놓이면 이 에러 대신 프로퍼티 핸들러가 먼저 받습니다;q.debug를 켜면 그 자리를 한 줄로 알려 줍니다) — 단Store·Context·Provider·Blocker·AttrKey는 어느 키에도 값으로 그대로 놓이지 않으니(숫자 키로 옮겨도 같은 에러) 메시지 꼬리대로 하세요 —Store는store.Name같은 필드를 놓거나q.Attr(store)로 감싸고,Context/Provider는 필요한 값을 먼저 읽어 내고,Blocker는State에:Apply하고,AttrKey는 키 자리([q.AttrKey(name)] = value)에 쓰세요. 설치 전이라면 그 값을 다루는 프로바이더가 설치돼 있는지 확인하세요. - 참고:
q.Dispatch.process(inst, key, value, index)
Quad0077
섹션 제목: “Quad0077”Dispatch: handler {describeHandler(h)} must return a retractor function at key {tostring(k)}, index {index} (got {typeof(retractor)}) - return Void when there is nothing to undo — quad-base/src/Dispatch/init.luau
- 언제: 매치된 핸들러의
process가 retractor(되돌리는 함수)를 돌려주지 않았을 때 — 프로바이더 계약 위반입니다.nil은 늘 검사하고, 함수가 아닌 다른 값(true·숫자·테이블,__call테이블 포함 —addHandler가process자체에 거는Quad0079와 같은 기준)은q.debug가 켜져 있을 때만 검사합니다(매process마다 도는 경로라 평소 비용을 더하지 않습니다).q.debug가 꺼져 있으면 그런 값은 저장됐다가 그 자리를 무를 때 quad 안에서 번호 없는 에러로 죽습니다.noRetractorMessage헬퍼가 만드는 문구이고,process의 두 갈래(같은 핸들러 재처리 / 다른 핸들러로 교체)의nil검사와 debug 검사가 모두 이 헬퍼를 부릅니다. - 고치려면: 그 핸들러를 만든 쪽이
process에서 항상 함수(할 일이 없으면q.Void)를 돌려주게 고치세요. 프로바이더를 만들 때는q.debug를 켜고 시험하세요. - 참고:
q.Dispatch.addHandler(handler)
Quad0079
섹션 제목: “Quad0079”Dispatch.addHandler: handler must be a table with isHandlable/process functions and a numeric priority (got {typeof(handler)}) — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.addHandler에 넘긴handler가 테이블이 아니거나,isHandlable/process가 함수가 아니거나,priority가 숫자가 아닐 때. - 고치려면:
{ isHandlable, process, priority, ... }모양의 핸들러 레코드를 넘기세요. - 참고:
q.Dispatch.addHandler(handler)
Quad0080
섹션 제목: “Quad0080”Dispatch.addHandler: priority must be a finite number (got {tostring(pr)}) — quad-base/src/Dispatch/init.luau
- 언제:
handler.priority가 NaN이거나 ±무한대일 때 — 그런 값은 정렬 순서를 정의할 수 없고 동률 검사(==)도 항상 거짓이 됩니다. - 고치려면:
HANDLER_PRIORITY_*밴드에서 오프셋한 유한한 숫자를 쓰세요. - 참고:
q.Dispatch.addHandler(handler)
Quad0081
섹션 제목: “Quad0081”Dispatch.addHandler: keyType must be "number", "string" or nil (got {tostring(kt)}) — quad-base/src/Dispatch/init.luau
- 언제:
handler.keyType이"number"/"string"/nil중 하나가 아닐 때 — 오타가 있으면 그 핸들러가 어느 버킷에도 안 들어가 조용히 매치되지 않게 됩니다. - 고치려면:
keyType은 생략하거나"number"/"string"중 하나로 쓰세요. - 참고:
q.Dispatch.addHandler(handler)
Quad0082
섹션 제목: “Quad0082”Dispatch.drive: props must be a table (got {typeof(flattened)}) — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.drive(inst, flattened)의flattened가 테이블이 아닐 때(백엔드의 생성 경로/Claim이 아니라 직접 부를 때 밟을 수 있는 자리입니다). - 고치려면: props 테이블을 넘기세요.
- 참고:
q.Dispatch.drive(inst, flattened)
Quad0083
섹션 제목: “Quad0083”Dispatch.drive: props must be a plain { ... } table - a quad value needs the braces (got {브랜드 이름들 또는 "a table with a metatable"}) — quad-base/src/Dispatch/init.luau
- 언제:
flattened자체가 메타테이블을 가진 값이거나, 메타테이블이 없는 quad 브랜드 값(q.AttrKey(...)·매퍼 디스크립터)일 때 —q.Elem "Frame" (q.Source(1))처럼 중괄호를 잊어 quad 값 하나를 그대로 props로 넘긴 흔한 실수를 잡습니다. 프로바이더가 등록한 브랜드 술어(q.BrandPredicates)까지 훑는 검사는q.debug가 켜져 있을 때만 돕니다 — 메타테이블 없는 평범한 테이블에 브랜드를 붙이는 프로바이더 값은 debug를 꺼 두면 여기 걸리지 않고 그 필드들이 props로 해석됩니다(대개Quad0076). quad-base 값은 전부 debug와 무관하게 걸립니다. - 고치려면: 항상 중괄호로 감싼 평범한 테이블을 넘기세요 —
q.Elem "Frame" { Name = q.Source("x") },q.Elem "Frame" { q.Attr{...} },q.Elem "Frame" { child }처럼. (q.Elem "Frame" { q.Source(1) }은 감싸도Quad0076입니다 — 숫자 키에 놓인Source<number>를 받는 핸들러가 없습니다.) - 참고:
q.Dispatch.drive(inst, flattened)
Quad0084
섹션 제목: “Quad0084”Dispatch.drive: inst must not be nil — quad-base/src/Dispatch/init.luau
- 언제:
q.Dispatch.drive를inst = nil로 부를 때. - 고치려면: 실제 대상 요소를 넘기세요.
- 참고:
q.Dispatch.drive(inst, flattened)
Quad0085
섹션 제목: “Quad0085”Dispatch.drive: array keys must be positive integers (got {k}) — quad-base/src/Dispatch/Modifier/init.luau
- 언제:
flattened의 숫자 키 중 하나가 1 이상의 정수가 아닐 때(0, 음수, 소수 등) — 어떤 핸들러가 부기를 만지기 전에 먼저 봅니다. - 고치려면: 배열부 키는 1부터 시작하는 정수만 쓰세요(중간의
nil구멍은 별개로 정의되지 않은 동작입니다). - 참고:
q.Dispatch.drive(inst, flattened)
Quad0266
섹션 제목: “Quad0266”Dispatch.addHandler: debugOnly must be a boolean or nil (got {typeof(fd)}) — quad-base/src/Dispatch/init.luau
- 언제:
handler.debugOnly가true/false/nil이 아닐 때 —"yes"나1처럼 참 같은 값이 그대로 통과하면 그 핸들러가 조용히 평소(debug 아님) 스캔 목록에 남습니다(== true만 debug 전용 목록으로 옮겨지므로). - 고치려면:
debugOnly는 생략하거나true/false로 쓰세요. - 참고:
q.Dispatch.addHandler(handler)
Quad0267
섹션 제목: “Quad0267”Bookkeeping.recompute: rewound 10000 times in one pass - a subscriber keeps changing a length before the position it is told about (a Slot Offset callback that adds and removes on every value never settles) — quad-base/src/Bookkeeping.luau
- 언제: 한 번의 자리 계산이 1만 번 넘게 되감았을 때 — 자리 계산 도중 발행된
Offset을 받은 콜백이 그 앞자리의 길이를 바꿔 계산이 그 자리부터 다시 돌기를 끝없이 되풀이하는 경우입니다. 흔한 모양은slot.Offset을 보는 Observer·Effect가 값마다 옆 Slot의 원소를 넣었다 뺐다 하는 것입니다. 이 한도는 늘 켜져 있고(q.debug와 무관)Quad0263(Effect의 지연 재실행)과 같은 수입니다. 수렴하는 되감기는 정의된 동작이고(목록이 아무리 길어도 되감기가 끝나면 걸리지 않습니다), 수렴하지 않는 고리는 정의되지 않은 동작입니다 — 이 한도는 그 고리를 정의하는 것이 아니라 무한히 돌지 않고 끝나게 하는 장치입니다(slot.Offset). 에러는 그 계산을 시작한 줄(CRUD·:Set·setLength)을 가리킵니다. - 고치려면: 콜백이 넣고 빼는 조건이 한 값에서 멈추게 하세요 — 받은 값에 따라 넣기와 빼기가 번갈아 참이 되면 끝나지 않습니다. 넣을지 말지를 콜백이 바꾸는 그 길이에 기대지 않는 값(데이터 개수 등)으로 정하세요.
- 관측(계약 아님): 이 에러는 자리 계산 도중에 나므로 그 부모의 부기는 멈춘 채 남습니다(
slot.Offset의 caution). - 참고:
slot.Offset, Length/Offset 부기
Quad0273
섹션 제목: “Quad0273”Spread: the argument must be a plain table written like an element table (got {typeof(t)}) — quad-base/src/Dispatch/Spread.luau
- 언제:
q.Raw.Spread(…)에 평범한 테이블이 아닌 값을 넘겼을 때 — 아무것도 안 넘기거나(nil), quad 값(Modifier·State 등 메타테이블이 붙은 값)을 그대로 넘긴 경우입니다. Spread는 요소 테이블과 같은 모양의 테이블에서만 만들어집니다. - 고치려면: 요소에 쓰듯 중괄호 테이블을 넘기세요(
q.Raw.Spread { Visible = true, someRef }). 다른 Spread를 합치려면 그 테이블의 숫자 키 자리에 넣거나:Append를 쓰세요.
Quad0275
섹션 제목: “Quad0275”Spread:Append: this Spread is locked - it was already placed in an element or spread into another Spread (a Spread cannot be appended to itself) — quad-base/src/Dispatch/Spread.luau
- 언제: 이미 요소에 놓였거나 다른 Spread에 펼쳐진 Spread에
:Append를 불렀을 때, 또는s:Append(s)처럼 자기 자신을 넣었을 때. Spread는 놓이거나 펼쳐지는 순간 내용이 복사되므로, 그 뒤의:Append는 이미 복사된 쪽에 보이지 않습니다 — 그 어긋남을 막으려고 잠급니다. 잠긴 Spread를 읽거나 다시 놓는 것은 됩니다. - 고치려면: 더할 내용은 놓기 전에
:Append하세요. 놓은 뒤에 내용이 바뀌어야 하면 새 Spread를 만들어 그 요소에 함께 놓으세요.
Quad0276
섹션 제목: “Quad0276”Spread:Append: the argument must be a Spread (got {typeof(other)}) — quad-base/src/Dispatch/Spread.luau
- 언제:
:Append에 Spread가 아닌 값을 넘겼을 때 — 프로퍼티 테이블이나 Ref·Observer 같은 값을 바로 넘긴 경우입니다.:Append는 Spread만 받습니다. - 고치려면: 더할 내용을 Spread로 감싸 넘기세요(
s:Append(q.Raw.Spread { obs })).
폐기된 번호
섹션 제목: “폐기된 번호”업데이트로 폐기되어 더는 나오지 않는 에러 번호입니다. 옛 번호를 만났다면 아래 목록에서 지금 쓰는 번호를 찾으세요.
폐기된 번호 목록
- Quad0064 — 4.0.0부터 같은 실수는
Quad0057입니다. 옛 문구:Modifier: field "{k}" cannot hold a handler-layer value (Ref/Observer/Effect/Slot/Modifier/Spread/mapper descriptor) - Quad0264 — 4.0.0부터 같은 실수는
Quad0063입니다. 옛 문구:Modifier: the transform for field "{key}" returned a function - a field cannot hold a function (an event handler does not belong in a Modifier; put it as an inline key on the element, or wrap it in a State) - Quad0274 — 4.0.0부터 같은 실수는
Quad0068입니다. 옛 문구:Spread: a Spread cannot be the value of key "{tostring(k)}" - place it in the array part - Quad0277 — 4.0.0부터 같은 실수는
Quad0068입니다. 옛 문구:Dispatch.drive: a Spread cannot be the value of key "{tostring(k)}" - place it in the array part
답변함: 0 / 0 (0%)