1. Errores <SUBSCRIPT>
Puntos Clave
- Acceso a subíndice indefinido: Referencia a un nodo de array global o local que no existe
- Acceso fuera de rango con $LIST: Uso de $LIST o $LISTGET con una posición más allá de la longitud de la lista
- Cadena vacía como subíndice: Los subíndices de globales no pueden ser la cadena vacía ""
- Número incorrecto de argumentos: Pasar menos argumentos de los esperados puede causar errores de subíndice en el código llamado
- $DATA y $GET: Usar estas funciones para verificar existencia antes de acceder a subíndices
- $LISTLENGTH: Verificar la longitud de la lista antes de acceder a posiciones específicas
Notas Detalladas
Descripción General
Los errores <SUBSCRIPT> ocurren cuando el código intenta usar un subíndice inválido para referenciar un nodo de array global o local, o cuando las operaciones $LIST referencian posiciones que no existen en una estructura de lista. Estos son de los errores en tiempo de ejecución más comunes en ObjectScript.
Acceso a Nodos de Subíndice Indefinidos
// ERROR: Accessing a subscript that doesn't exist
Kill ^Data
Set ^Data(1) = "First"
Set ^Data(3) = "Third"
// This causes <SUBSCRIPT> if ^Data(2) doesn't exist
// and you use it in certain contexts
Write ^Data(2) // <SUBSCRIPT> error - node 2 doesn't exist
// FIX: Use $GET with a default value
Write $GET(^Data(2), "N/A"), ! // Returns "N/A" safely
// FIX: Check with $DATA first
If $DATA(^Data(2)) {
Write ^Data(2), !
}
Else {
Write "No data for subscript 2", !
}
Cadena Vacía como Subíndice
// ERROR: Empty string is not a valid global subscript
Set key = ""
Set ^Data(key) = "value" // <SUBSCRIPT> error
// FIX: Validate before use
ClassMethod StoreValue(key As %String, value As %String) As %Status
{
If key = "" {
Return $$$ERROR($$$GeneralError, "Key cannot be empty")
}
Set ^Data(key) = value
Return $$$OK
}
Acceso Fuera de Rango con $LIST
// ERROR: $LIST with out-of-range position
Set myList = $LISTBUILD("Apple", "Banana", "Cherry")
// myList has 3 elements
// Accessing position 5 causes <SUBSCRIPT> error with $LIST
Write $LIST(myList, 5) // <SUBSCRIPT> error
// FIX: Use $LISTGET (returns "" for missing positions)
Write $LISTGET(myList, 5), ! // Returns ""
Write $LISTGET(myList, 5, "N/A"), ! // Returns "N/A"
// FIX: Check length first
If $LISTLENGTH(myList) >= 5 {
Write $LIST(myList, 5), !
}
Else {
Write "List has only ", $LISTLENGTH(myList), " elements", !
}
Comparación entre $LIST y $LISTGET
Set list = $LISTBUILD("A", "B", "C")
// $LIST - throws <SUBSCRIPT> if position invalid
// Use when you KNOW the position exists
Set first = $LIST(list, 1) // "A" - safe
// Set fifth = $LIST(list, 5) // <SUBSCRIPT> error!
// $LISTGET - returns default if position invalid
// Use when position may not exist
Set first = $LISTGET(list, 1) // "A"
Set fifth = $LISTGET(list, 5) // "" (empty string)
Set fifth = $LISTGET(list, 5, "?") // "?" (custom default)
Número Incorrecto de Argumentos
// ERROR: Method expects data in a subscripted array
ClassMethod ProcessItems(ByRef items)
{
// Expects items(1), items(2), etc.
For i = 1:1:items {
Write items(i), ! // <SUBSCRIPT> if items(i) doesn't exist
}
}
// Caller passes wrong structure
ClassMethod CallerBug()
{
Set data = 3 // Set count
Set data(1) = "Apple"
Set data(2) = "Banana"
// Forgot to set data(3)!
Do ..ProcessItems(.data) // <SUBSCRIPT> at items(3)
}
// FIX: Defensive coding with $GET
ClassMethod ProcessItemsSafe(ByRef items)
{
For i = 1:1:$GET(items, 0) {
Set value = $GET(items(i), "")
If value '= "" {
Write value, !
}
}
}
Referencias de Documentación
2. Errores <PROTECT>
Puntos Clave
- Base de datos de solo lectura: Escritura en una global en una base de datos de solo lectura o montada como solo lectura
- Permisos insuficientes: El proceso carece de los privilegios de recurso necesarios para acceder a una base de datos
- Acceso denegado al namespace: El proceso no tiene permiso para cambiar a o operar en un namespace
- Rol sin recurso de base de datos: Los roles del usuario no incluyen acceso de Lectura-Escritura al recurso de la base de datos
- Globales del sistema: Ciertas globales del sistema están protegidas y no pueden ser modificadas por código de aplicación
- Resolución: Verificar las propiedades de la base de datos, los roles del usuario y las asignaciones de recursos en el Management Portal
Notas Detalladas
Descripción General
Los errores <PROTECT> ocurren cuando un proceso intenta una operación que viola las restricciones de seguridad o los permisos de acceso a la base de datos. Estos errores están relacionados con la seguridad y requieren comprender el modelo de seguridad de InterSystems IRIS: recursos, roles y permisos de base de datos.
Escritura en una Base de Datos de Solo Lectura
// ERROR: Database is mounted read-only
// (e.g., IRISSYS or a database mounted for backup)
Set ^SomeGlobal = "value" // <PROTECT> if database is read-only
// Checking if you can write to the current namespace:
ClassMethod CanWrite() As %Boolean
{
TRY {
Set testKey = "##TEST##" _ $JOB
Set ^CacheTemp(testKey) = 1
Kill ^CacheTemp(testKey)
Return 1
}
CATCH ex {
If ex.Name = "<PROTECT>" {
Return 0
}
THROW ex
}
}
Manejo de en Código de Aplicación
ClassMethod SaveData(key As %String, value As %String) As %Status
{
TRY {
Set ^AppData(key) = value
Return $$$OK
}
CATCH ex {
If ex.Name = "<PROTECT>" {
// Provide a clear message to the user
Return $$$ERROR($$$GeneralError,
"Cannot write to database. Check that the database " _
"is not read-only and that your account has " _
"Read-Write access to the database resource.")
}
Return ex.AsStatus()
}
}
Causas Comunes y Resoluciones
Causa: Base de datos de solo lectura
Verificar: Management Portal > System Administration > Configuration >
System Configuration > Local Databases
Solución: Cambiar la base de datos de Read-Only a Read-Write
Causa: El usuario carece de permiso sobre el recurso de la base de datos
Verificar: Management Portal > System Administration > Security >
Users > [usuario] > pestaña Roles
Solución: Agregar un rol que tenga Read-Write sobre el recurso de la base de datos
Causa: La aplicación se ejecuta como UnknownUser con privilegios mínimos
Verificar: Management Portal > System Administration > Security >
Users > UnknownUser
Solución: Asignar roles apropiados o configurar la aplicación para
autenticarse correctamente
Causa: La base de datos predeterminada del namespace difiere de la esperada
Verificar: Management Portal > System Administration > Configuration >
System Configuration > Namespaces
Solución: Verificar el mapeo de la base de datos de globales para el namespace
Verificación Programática de Seguridad
ClassMethod CheckDatabaseAccess(namespace As %String) As %String
{
// Get the default database for a namespace
Set db = ##class(%SYS.Namespace).GetGlobalDest(namespace)
Write "Global database: ", db, !
// Check current user's roles
Write "Current user: ", $USERNAME, !
Write "Current roles: ", $ROLES, !
Return db
}
Referencias de Documentación
3. Errores <FRAMESTACK>
Puntos Clave
- Recursión infinita: Causa más común; un método se llama a sí mismo sin un caso base adecuado
- Profundidad de llamadas excesiva: Incluso código no recursivo puede agotar la pila de marcos con cadenas de llamadas profundamente anidadas
- Recursión indirecta: El Método A llama al Método B, que llama al Método A, creando un bucle infinito
- Pila de marcos: Pila de tamaño fijo que almacena direcciones de retorno y contexto para cada llamada a método
- Caso base: Todo método recursivo debe tener una condición que detenga la recursión
- Alternativas iterativas: Convertir recursión profunda a bucles iterativos cuando sea posible
Notas Detalladas
Descripción General
Los errores <FRAMESTACK> ocurren cuando la pila de llamadas de ejecución se desborda. Cada llamada a método, comando DO o XECUTE empuja un marco a la pila. Cuando la pila se agota (típicamente después de miles de llamadas anidadas), IRIS genera un error <FRAMESTACK>. La causa más común es la recursión infinita.
Recursión Infinita - Directa
// ERROR: No base case - infinite recursion
ClassMethod BadFactorial(n As %Integer) As %Integer
{
// Missing: If n <= 1 Return 1
Return n * ..BadFactorial(n - 1) // Never stops!
// Calls: BadFactorial(5) -> BadFactorial(4) -> ... -> BadFactorial(-9999) -> <FRAMESTACK>
}
// FIX: Add a proper base case
ClassMethod Factorial(n As %Integer) As %Integer
{
If n <= 1 {
Return 1 // BASE CASE - stops recursion
}
Return n * ..Factorial(n - 1)
}
Recursión Infinita - Indirecta
// ERROR: Indirect recursion - A calls B, B calls A
ClassMethod ProcessA(data As %String)
{
// Some processing...
Set data = data _ "A"
Do ..ProcessB(data) // Calls B
}
ClassMethod ProcessB(data As %String)
{
// Some processing...
Set data = data _ "B"
Do ..ProcessA(data) // Calls A -> infinite loop!
}
// FIX: Add termination condition
ClassMethod ProcessA(data As %String, depth As %Integer = 0)
{
If depth > 100 {
Write "Maximum processing depth reached", !
Return
}
Set data = data _ "A"
Do ..ProcessB(data, depth + 1)
}
ClassMethod ProcessB(data As %String, depth As %Integer = 0)
{
If depth > 100 {
Write "Maximum processing depth reached", !
Return
}
Set data = data _ "B"
Do ..ProcessA(data, depth + 1)
}
Conversión de Recursión a Iteración
// RECURSIVE version - may cause <FRAMESTACK> for deep trees
ClassMethod TraverseTree(node As %String)
{
Quit:node=""
Write node, !
Set child = $ORDER(^Tree(node, ""))
While child '= "" {
Do ..TraverseTree(child) // Recursive call
Set child = $ORDER(^Tree(node, child))
}
}
// ITERATIVE version - uses an explicit stack (no <FRAMESTACK> risk)
ClassMethod TraverseTreeIterative(startNode As %String)
{
// Use a local array as an explicit stack
Set stackTop = 1
Set stack(stackTop) = startNode
While stackTop > 0 {
// Pop from stack
Set node = stack(stackTop)
Set stackTop = stackTop - 1
If node = "" Continue
Write node, !
// Push children onto stack
Set child = $ORDER(^Tree(node, ""))
While child '= "" {
Set stackTop = stackTop + 1
Set stack(stackTop) = child
Set child = $ORDER(^Tree(node, child))
}
}
}
Captura de Errores
ClassMethod SafeRecursion(data As %String) As %Status
{
TRY {
Do ..DeepRecursiveProcess(data, 0)
Return $$$OK
}
CATCH ex {
If ex.Name = "<FRAMESTACK>" {
Return $$$ERROR($$$GeneralError,
"Operation exceeded maximum recursion depth. " _
"Data may be too deeply nested.")
}
Return ex.AsStatus()
}
}
ClassMethod DeepRecursiveProcess(data As %String, depth As %Integer)
{
// Guard clause: prevent <FRAMESTACK> with explicit limit
If depth > 500 {
Throw ##class(%Exception.General).%New(
"MaxDepth", 5001, , "Recursion depth exceeded 500")
}
// ... recursive processing ...
}
Referencias de Documentación
4. Errores <UNDEFINED>
Puntos Clave
- Variable local indefinida: Uso de una variable a la que nunca se le asignó un valor
- Global indefinida: Referencia a un nodo de global que no existe (lectura, no escritura)
- Error tipográfico en nombre de variable: Un error de escritura en el nombre de una variable crea una variable diferente (indefinida)
- Variable no inicializada: La ruta de código alcanza el uso de una variable antes de su asignación
- $GET para prevenir: Usar $GET(variable, predeterminado) para acceder de forma segura a variables potencialmente indefinidas
- $DATA para verificar: Usar $DATA(variable) para probar la existencia antes de acceder
Notas Detalladas
Descripción General
<UNDEFINED> es el error en tiempo de ejecución más común en ObjectScript. Ocurre cuando el código intenta leer el valor de una variable (local o global) que no tiene un valor asignado. El mensaje de error incluye el nombre de la variable, lo que hace relativamente fácil el diagnóstico. La prevención se basa en prácticas de codificación defensiva.
Variable Local Indefinida
// ERROR: Variable never assigned
ClassMethod UndefinedExample()
{
// Write result // <UNDEFINED> - 'result' was never set
// Common pattern: conditional assignment
Set inputValue = ""
If inputValue '= "" {
Set processed = $ZCONVERT(inputValue, "U")
}
// ERROR: If inputValue was "", 'processed' was never set
Write processed // <UNDEFINED> *processed
// FIX: Initialize before conditional
Set processed = ""
If inputValue '= "" {
Set processed = $ZCONVERT(inputValue, "U")
}
Write processed, ! // Safe - always has a value
}
Errores Tipográficos en Nombres de Variables
// ERROR: Typo creates a different variable
ClassMethod TypoExample()
{
Set customerName = "John Doe"
// ... many lines later ...
// Typo: 'custormerName' instead of 'customerName'
Write custormerName // <UNDEFINED> *custormerName
// ObjectScript is case-sensitive for variable names!
Set myValue = 42
Write MyValue // <UNDEFINED> *MyValue (capital M!)
Write myvalue // <UNDEFINED> *myvalue (lowercase v!)
Write myValue, ! // Correct: 42
}
Variable No Inicializada en Todas las Rutas de Código
// ERROR: Variable only set on some branches
ClassMethod ConditionalBug(type As %String) As %String
{
If type = "A" {
Set result = "Type A processing"
}
ElseIf type = "B" {
Set result = "Type B processing"
}
// What if type = "C"? 'result' is never set!
Return result // <UNDEFINED> *result when type = "C"
// FIX: Initialize with default before conditionals
}
ClassMethod ConditionalFixed(type As %String) As %String
{
Set result = "Unknown type: " _ type // Default value
If type = "A" {
Set result = "Type A processing"
}
ElseIf type = "B" {
Set result = "Type B processing"
}
Return result // Always has a value
}
Uso de $GET para Prevenir
ClassMethod SafeAccessExamples()
{
// $GET with local variables
Set name = $GET(undefinedVar) // Returns ""
Set name = $GET(undefinedVar, "N/A") // Returns "N/A"
// $GET with global variables
Set config = $GET(^Config("timeout"), 30) // Default 30 if not set
// $GET with object properties (when object might be "")
Set obj = ##class(MyApp.Person).%OpenId(999)
// If ID 999 doesn't exist, obj = ""
// obj.Name would cause an error, but:
If obj '= "" {
Set name = obj.Name
}
Else {
Set name = "Not found"
}
// $GET with multidimensional array
Set data(1) = "first"
Set data(3) = "third"
// data(2) is not set
For i = 1:1:3 {
Write i, ": ", $GET(data(i), "(empty)"), !
}
// Output:
// 1: first
// 2: (empty)
// 3: third
}
Uso de $DATA para Verificar Existencia
ClassMethod DataCheckExamples()
{
Set ^Config("host") = "localhost"
Set ^Config("port") = 8080
// ^Config("password") is not set
// $DATA returns:
// 0 - node does not exist
// 1 - node has data, no children
// 10 - node has children, no data
// 11 - node has both data and children
If $DATA(^Config("password")) {
Set pw = ^Config("password")
}
Else {
Write "Password not configured!", !
}
// Compact form using $DATA's truthiness
Set host = $SELECT($DATA(^Config("host")):^Config("host"), 1:"127.0.0.1")
}
Referencia a Global Indefinida
// ERROR: Reading a global that doesn't exist
Kill ^TempData
Write ^TempData(1) // <UNDEFINED> *^TempData(1)
// FIX: Use $GET
Write $GET(^TempData(1), "not set"), !
// FIX: Use $DATA
If $DATA(^TempData(1)) {
Write ^TempData(1), !
}
// NOTE: Setting a global never causes <UNDEFINED>
Set ^TempData(1) = "value" // Always works (assuming no <PROTECT>)
Patrón Defensivo Completo
Include %occStatus
ClassMethod ProcessRecord(id As %Integer) As %Status
{
TRY {
// Defensive: validate inputs
If '$DATA(id) || (id = "") {
Return $$$ERROR($$$GeneralError, "ID is required")
}
// Defensive: check object exists before using
Set obj = ##class(MyApp.Record).%OpenId(id, , .sc)
$$$ThrowOnError(sc)
If obj = "" {
Return $$$ERROR($$$GeneralError, "Record not found: " _ id)
}
// Defensive: use $GET for optional properties
Set category = $GET(obj.Category, "General")
Set priority = $GET(obj.Priority, 3)
// Defensive: use $GET for global lookups
Set config = $GET(^AppConfig("maxRetries"), 5)
Return $$$OK
}
CATCH ex {
If ex.Name = "<UNDEFINED>" {
Return $$$ERROR($$$GeneralError,
"Undefined variable: " _ ex.Data _ " at " _ ex.Location)
}
Return ex.AsStatus()
}
}
Referencias de Documentación
5. Errores <INVALID OREF>
Puntos Clave
- Llamar método en un no-objeto: Uso de sintaxis de punto en un valor que no es una referencia de objeto válida (cadena vacía, número)
- %OpenId fallido: `%OpenId()` devuelve `""` cuando el ID no existe; llamar un método en ese resultado dispara el error
- OREF expirada: La referencia de objeto se vuelve inválida después de que el objeto se cierra o sale del alcance
- Verificación con $ISOBJECT: Siempre verificar que una OREF es válida con `$ISOBJECT()` antes de acceder a propiedades o métodos
- Verificación con %Status: Usar el parámetro de salida de estado de `%OpenId()` para detectar y manejar fallos de apertura
- Patrón común: Abrir, verificar, usar; nunca asumir que `%OpenId()` tiene éxito
Notas Detalladas
Descripción General
Los errores <INVALID OREF> ocurren cuando el código intenta llamar un método o acceder a una propiedad en un valor que no es una referencia de objeto válida. La causa más común es usar el resultado de %OpenId() sin verificar si devolvió un objeto válido. Si el ID solicitado no existe, %OpenId() devuelve "", y cualquier acceso posterior con sintaxis de punto en ese valor dispara <INVALID OREF>.
%OpenId Fallido que Conduce a INVALID OREF
// ERROR: %OpenId returns "" when ID doesn't exist
ClassMethod DisplayPerson(id As %Integer)
{
Set person = ##class(MyApp.Person).%OpenId(id)
// If id 999 doesn't exist, person = ""
Write person.Name, ! // <INVALID OREF> error!
}
// FIX: Check with $ISOBJECT before use
ClassMethod DisplayPersonSafe(id As %Integer) As %Status
{
Set person = ##class(MyApp.Person).%OpenId(id)
If '$ISOBJECT(person) {
Write "Person not found: ", id, !
Return $$$ERROR($$$GeneralError, "Person not found: " _ id)
}
Write person.Name, !
Return $$$OK
}
Referencia de Objeto Expirada o Eliminada
// ERROR: OREF becomes invalid after object is killed
ClassMethod OrefScopeIssue()
{
Set obj = ##class(%ArrayOfDataTypes).%New()
Do obj.SetAt("value1", "key1")
// Kill the object
Set obj = ""
// Now obj is "" (empty string), not an OREF
Do obj.SetAt("value2", "key2") // <INVALID OREF> error!
// FIX: Check before use
If $ISOBJECT(obj) {
Do obj.SetAt("value2", "key2")
}
}
Uso del Parámetro de Salida %Status
// BEST PRACTICE: Use the status parameter of %OpenId
ClassMethod ProcessRecord(id As %Integer) As %Status
{
Set record = ##class(MyApp.Record).%OpenId(id, , .sc)
If $$$ISERR(sc) {
// Open failed - sc contains the reason
Return sc
}
If '$ISOBJECT(record) {
Return $$$ERROR($$$GeneralError, "Record not found: " _ id)
}
// Safe to use record
Set record.Status = "Processed"
Return record.%Save()
}
Referencias de Documentación
6. Errores <CLASS DOES NOT EXIST>
Puntos Clave
- Nombre de clase mal escrito: Un error tipográfico en el nombre de clase usado con `##class()` causa un fallo inmediato
- Nombre de paquete faltante: Uso de un nombre de clase corto sin la ruta completa del paquete
- Clase no compilada: La clase existe en el código fuente pero no ha sido compilada aún
- Sensibilidad a mayúsculas y minúsculas: Los nombres de clase en ObjectScript son sensibles a mayúsculas y minúsculas
- Verificar con %Dictionary: Usar `##class(%Dictionary.ClassDefinition).%ExistsId()` para verificar en tiempo de ejecución
- Autocompletado del IDE: Usar Studio o VS Code con extensiones de ObjectScript para prevenir errores tipográficos
Notas Detalladas
Descripción General
Los errores <CLASS DOES NOT EXIST> ocurren cuando el código referencia una clase que no se puede encontrar en el diccionario de clases. Esto típicamente sucede debido a errores tipográficos en llamadas ##class(), prefijos de paquete faltantes, o referencia a una clase que no ha sido compilada. El error incluye el nombre de la clase que no se encontró, lo que hace que el diagnóstico sea directo.
Error Tipográfico en Nombre de Clase
// ERROR: Misspelled class name
ClassMethod CreatePerson() As %Status
{
// Typo: "Perso" instead of "Person"
Set person = ##class(MyApp.Perso).%New() // <CLASS DOES NOT EXIST>
// FIX: Correct the class name
Set person = ##class(MyApp.Person).%New()
}
Prefijo de Paquete Faltante
// ERROR: Missing the full package path
ClassMethod OpenRecord() As %Status
{
// Missing "MyApp." package prefix
Set obj = ##class(Person).%New() // <CLASS DOES NOT EXIST>
// FIX: Include the full package name
Set obj = ##class(MyApp.Person).%New()
}
Ruta de Clase Incorrecta en Llamadas del Sistema
// ERROR: Wrong class hierarchy for system utilities
// Common mistake with SQL statistics gathering
Do $SYSTEM.SQL.Stats.GatherTableStats("MyApp.Person")
// <CLASS DOES NOT EXIST> if the correct path differs
// FIX: Use the correct class path
Do $SYSTEM.SQL.Stats.Table.GatherTableStats("MyApp.Person")
Verificación de Existencia de Clase en Tiempo de Ejecución
// Checking if a class exists before using it
ClassMethod SafeCreateObject(className As %String) As %RegisteredObject
{
// Check if the class exists in the dictionary
If '##class(%Dictionary.ClassDefinition).%ExistsId(className) {
Write "Class does not exist: ", className, !
Return ""
}
// Safe to create an instance
Set obj = $CLASSMETHOD(className, "%New")
Return obj
}
Referencias de Documentación
7. Errores <METHOD DOES NOT EXIST>
Puntos Clave
- Nombre de método mal escrito: Error tipográfico en el nombre del método al llamarlo en un objeto o clase
- Prefijo % faltante: Muchos métodos del sistema comienzan con `%` (por ejemplo, `%Save()` no `Save()`)
- Método de instancia vs método de clase: Llamar un método de instancia como método de clase o viceversa
- Método no está en la clase: El método existe en una clase o superclase diferente a la esperada
- $CLASSMETHOD/$METHOD: Estas funciones del sistema pueden disparar este error con nombres de método incorrectos
- Verificar definición de clase: Verificar que el método existe y su tipo (instancia vs clase) antes de llamarlo
Notas Detalladas
Descripción General
Los errores <METHOD DOES NOT EXIST> ocurren cuando el código intenta llamar un método que no está definido en la clase objetivo (ni en ninguna de sus superclases). Las causas más comunes son errores tipográficos en nombres de métodos, olvidar el prefijo % en métodos del sistema, y confundir métodos de instancia con métodos de clase. El mensaje de error incluye tanto el nombre del método como el nombre de la clase.
Prefijo % Faltante en Métodos del Sistema
// ERROR: Forgetting the % prefix on system methods
ClassMethod SavePerson() As %Status
{
Set person = ##class(MyApp.Person).%New()
Set person.Name = "John"
// Wrong: Save() does not exist - the method is %Save()
Set sc = person.Save() // <METHOD DOES NOT EXIST>
// FIX: Use the correct method name with % prefix
Set sc = person.%Save()
Return sc
}
Método de Instancia Llamado como Método de Clase
// ERROR: Calling an instance method as a class method
ClassMethod ProcessExample()
{
// %Save is an instance method, not a class method
Set sc = ##class(MyApp.Person).%Save() // <METHOD DOES NOT EXIST>
// FIX: Create or open an instance first, then call the method
Set person = ##class(MyApp.Person).%New()
Set person.Name = "Jane"
Set sc = person.%Save() // Correct: called on instance
}
Error Tipográfico en Nombre de Método
// ERROR: Misspelled method name
ClassMethod QueryExample()
{
Set stmt = ##class(%SQL.Statement).%New()
// Wrong: "Preapre" instead of "Prepare"
Set sc = stmt.Preapre("SELECT Name FROM MyApp.Person") // <METHOD DOES NOT EXIST>
// FIX: Correct spelling
Set sc = stmt.%Prepare("SELECT Name FROM MyApp.Person")
}
Uso Seguro de $CLASSMETHOD
// Dynamic method dispatch - verify method exists first
ClassMethod DynamicCall(className As %String, methodName As %String) As %String
{
// Check if the method exists in the class
Set methodDef = className _ "||" _ methodName
If '##class(%Dictionary.MethodDefinition).%ExistsId(methodDef) {
Write "Method does not exist: ", className, ".", methodName, !
Return ""
}
// Safe to call
Return $CLASSMETHOD(className, methodName)
}
Referencias de Documentación
8. Errores <PROPERTY DOES NOT EXIST>
Puntos Clave
- Nombre de propiedad mal escrito: Error tipográfico en el nombre de la propiedad al acceder mediante sintaxis de punto
- Suposición de clase incorrecta: El objeto es de una clase diferente a la esperada, careciendo de la propiedad
- Sensibilidad a mayúsculas y minúsculas: Los nombres de propiedad son sensibles a mayúsculas y minúsculas en ObjectScript
- Acceso dinámico a propiedades: Uso de `$PROPERTY()` con un nombre de propiedad incorrecto
- Verificar definición de clase: Usar `%Dictionary.PropertyDefinition` para verificar la existencia de propiedades en tiempo de ejecución
- Propiedades calculadas: Algunas propiedades pueden ser calculadas y no almacenadas, pero siguen existiendo en la definición de clase
Notas Detalladas
Descripción General
Los errores <PROPERTY DOES NOT EXIST> ocurren cuando el código intenta acceder a una propiedad que no está definida en la clase del objeto. Esto es similar a <METHOD DOES NOT EXIST> pero para propiedades. El mensaje de error incluye el nombre de la propiedad y el nombre de la clase. Las causas comunes incluyen errores tipográficos, problemas de sensibilidad a mayúsculas y minúsculas, y acceso a una propiedad en la clase incorrecta.
Error Tipográfico en Nombre de Propiedad
// ERROR: Misspelled property name
ClassMethod DisplayPerson(id As %Integer)
{
Set person = ##class(MyApp.Person).%OpenId(id)
If '$ISOBJECT(person) Return
// Wrong: "Naem" instead of "Name"
Write person.Naem, ! // <PROPERTY DOES NOT EXIST>
// FIX: Correct the property name
Write person.Name, !
}
Suposición de Clase Incorrecta
// ERROR: Assuming the wrong class
ClassMethod ProcessItem(item As %RegisteredObject)
{
// Caller passes a %ArrayOfDataTypes but code assumes MyApp.Person
Write item.Name, ! // <PROPERTY DOES NOT EXIST> if item is not a Person
// FIX: Check the class before accessing specific properties
If item.%IsA("MyApp.Person") {
Write item.Name, !
}
Else {
Write "Unexpected object type: ", item.%ClassName(1), !
}
}
Acceso Dinámico a Propiedades con $PROPERTY
// ERROR: Wrong property name in dynamic access
ClassMethod GetPropertyValue(obj As %RegisteredObject, propName As %String) As %String
{
// If propName is wrong, this triggers <PROPERTY DOES NOT EXIST>
Return $PROPERTY(obj, propName)
}
// FIX: Validate property existence first
ClassMethod GetPropertyValueSafe(obj As %RegisteredObject, propName As %String) As %String
{
Set className = obj.%ClassName(1)
Set propDef = className _ "||" _ propName
If '##class(%Dictionary.PropertyDefinition).%ExistsId(propDef) {
Write "Property does not exist: ", className, ".", propName, !
Return ""
}
Return $PROPERTY(obj, propName)
}
Referencias de Documentación
9. Errores <SYNTAX>
Puntos Clave
- Detección en tiempo de compilación: La mayoría de los errores de sintaxis se detectan cuando se compila la clase o rutina
- En tiempo de ejecución con XECUTE: Los errores de sintaxis en código ejecutado dinámicamente (`XECUTE`, `$XECUTE`) ocurren en tiempo de ejecución
- Delimitadores de cierre faltantes: Paréntesis, llaves o comillas sin emparejar son causas comunes
- Operador incorrecto: Uso de un operador inválido o un operador en el contexto incorrecto
- Comillas de cadenas: Comillas faltantes o desemparejadas en literales de cadena
- Indirección: Los errores de sintaxis en expresiones de indirección `@variable` se detectan en tiempo de ejecución
Notas Detalladas
Descripción General
Los errores <SYNTAX> indican que el analizador de ObjectScript no puede interpretar el código debido a un problema estructural. La mayoría de los errores de sintaxis se detectan en tiempo de compilación, evitando que el código se ejecute. Sin embargo, los errores de sintaxis también pueden ocurrir en tiempo de ejecución al usar ejecución dinámica de código (comando XECUTE, función $XECUTE) o indirección (@), ya que estos construyen y analizan cadenas de código en tiempo de ejecución.
Paréntesis de Cierre Faltante
// ERROR: Missing closing parenthesis (caught at compile time)
ClassMethod CompileError()
{
Set x = $LENGTH("hello" // <SYNTAX> - missing closing )
// FIX: Match all parentheses
Set x = $LENGTH("hello")
}
Llaves Desbalanceadas
// ERROR: Missing closing brace (caught at compile time)
ClassMethod BraceError()
{
If x = 1 {
Write "one", !
// Missing closing } // <SYNTAX>
// FIX: Ensure every { has a matching }
If x = 1 {
Write "one", !
}
}
Error de Sintaxis en Tiempo de Ejecución con XECUTE
// ERROR: Syntax error in dynamic code (caught at RUNTIME)
ClassMethod DynamicCodeError()
{
Set code = "Set x = $LENGTH(""hello""" // Missing closing )
// This compiles fine - the string is valid
// But executing it causes <SYNTAX> at runtime
XECUTE code // <SYNTAX> error at runtime!
// FIX: Validate dynamic code carefully
Set code = "Set x = $LENGTH(""hello"")"
XECUTE code // Works correctly
}
Error de Sintaxis con Indirección
// ERROR: Invalid expression in indirection
ClassMethod IndirectionError()
{
Set expr = "1 + + 2" // Invalid expression
Set result = @expr // <SYNTAX> at runtime
// FIX: Validate the expression
Set expr = "1 + 2"
Set result = @expr // Works: result = 3
}
Errores de Sintaxis Comunes
// Common syntax errors and their fixes:
// 1. Missing dot before method call in same class
// Do MyMethod() // <SYNTAX> in instance context
// Do ..MyMethod() // Correct for instance methods
// 2. Wrong string quoting (doubling quotes inside strings)
// Set x = "He said "hello"" // <SYNTAX>
// Set x = "He said ""hello""" // Correct
// 3. Missing comma in function arguments
// Set x = $EXTRACT(str 1 3) // <SYNTAX>
// Set x = $EXTRACT(str, 1, 3) // Correct
// 4. Using = instead of == in expressions (not an error but a logic bug)
// Note: ObjectScript uses = for both assignment and comparison
// depending on context, so this is valid syntax
Referencias de Documentación
Resumen de Preparación para el Examen
Conceptos Críticos a Dominar:
: Saber que $LIST lanza para fuera de rango pero $LISTGET devuelve "" de forma segura : Comprender la relación entre bases de datos, recursos, roles y permisos de acceso : Reconocer la recursión infinita (directa e indirecta) como la causa principal : Saber que leer (no escribir) una variable indefinida causa el error, y ex.Data contiene el nombre de la variable : Ocurre al llamar un método o propiedad en un no-objeto; siempre verificar `$ISOBJECT()` después de `%OpenId()` : Errores tipográficos en `##class()`, nombres de paquete faltantes, o clases no compiladas : Prefijo `%` faltante en métodos del sistema, confusión entre método de instancia y de clase : Errores tipográficos en nombres de propiedad, suposición de clase incorrecta, sensibilidad a mayúsculas y minúsculas : Usualmente detectado en tiempo de compilación, pero puede ocurrir en tiempo de ejecución con `XECUTE` o indirección - $GET como prevención: $GET es la herramienta defensiva principal para prevenir tanto
como - $DATA para verificación: $DATA devuelve 0 para nodos inexistentes, no cero para existentes
- Nombres de error en ex.Name: Conocer el formato con corchetes angulares:
, , , , , etc. - Casos base: Todo método recursivo debe tener una condición que termine la recursión
Escenarios Comunes de Examen:
- Dado un código que accede a un subíndice de global, determinar si puede ocurrir un error
- Identificar la causa de un error
(base de datos de solo lectura, rol faltante, permiso de recurso) - Detectar recursión infinita en código e identificar la solución (agregar caso base)
- Reconocer que una variable usada en una rama de un IF pero no en otra puede causar
- Elegir entre $GET y $DATA para un escenario dado de codificación defensiva
- Determinar qué función $LIST es segura para accesos potencialmente fuera de rango ($LISTGET)
- Identificar problemas de sensibilidad a mayúsculas y minúsculas en nombres de variables que conducen a
- Reconocer que `%OpenId()` puede devolver `""` y que usar sintaxis de punto en ello causa
- Detectar errores tipográficos en nombres de clase, método o propiedad que causan errores "DOES NOT EXIST"
- Distinguir entre errores
en tiempo de compilación y en tiempo de ejecución por XECUTE/indirección - Saber que `Save()` es incorrecto pero `%Save()` es correcto (patrón de prefijo % faltante)
Recomendaciones de Práctica:
- Causar deliberadamente cada tipo de error y examinar el objeto de excepción
- Escribir métodos recursivos con y sin casos base para observar
- Crear código que use $GET y $DATA de forma defensiva, luego quitar las protecciones para ver los errores
- Configurar una base de datos de solo lectura e intentar escribir en ella para observar
- Escribir un método con un error tipográfico en el nombre de una variable y observar el error
con ex.Data - Practicar la conversión de algoritmos recursivos a iterativos usando pilas explícitas
- Usar $LISTGET vs $LIST con posiciones fuera de rango para comprender la diferencia
- Abrir un ID inexistente con `%OpenId()` e intentar acceder a una propiedad para ver
- Escribir deliberadamente mal un nombre de clase en `##class()` para ver
- Llamar `Save()` en lugar de `%Save()` para ver
- Usar XECUTE con código malformado para observar errores
en tiempo de ejecución - Construir un manejador de errores completo que identifique cada tipo de error y proporcione orientación específica